Massive updates. %r -> %s, some commands.wrap updates. Factoids is broke

until we get some stuff working in commands.py
This commit is contained in:
James Vega 2004-10-23 22:07:50 +00:00
parent 7ab8be2f64
commit 4dafdcdd57
35 changed files with 407 additions and 314 deletions

View File

@ -225,7 +225,8 @@ class Alias(callbacks.Privmsg):
raise AliasError, 'Names cannot coincide with names of plugins.'
realName = callbacks.canonicalName(name)
if name != realName:
s = 'That name isn\'t valid. Try %r instead.' % realName
s = 'That name isn\'t valid. Try %s instead.' % \
utils.quoted(realName)
raise AliasError, s
name = realName
cbs = callbacks.findCallbackForCommand(irc, name)
@ -236,7 +237,7 @@ class Alias(callbacks.Privmsg):
if name in self.aliases:
(currentAlias, locked) = self.aliases[name]
if locked and currentAlias != alias:
raise AliasError, 'Alias %r is locked.' % name
raise AliasError, 'Alias %s is locked.' % utils.quoted(name)
try:
f = makeNewAlias(name, alias)
except RecursiveAlias:
@ -280,8 +281,8 @@ class Alias(callbacks.Privmsg):
alias += ' $*'
try:
self.addAlias(irc, name, alias)
self.log.info('Adding alias %r for %r (from %s)' %
(name, alias, msg.prefix))
self.log.info('Adding alias %s for %s (from %s)' %
(utils.quoted(name), utils.quoted(alias),msg.prefix))
irc.replySuccess()
except AliasError, e:
irc.error(str(e))
@ -294,7 +295,8 @@ class Alias(callbacks.Privmsg):
name = privmsgs.getArgs(args)
try:
self.removeAlias(name)
self.log.info('Removing alias %r (from %s)' % (name, msg.prefix))
self.log.info('Removing alias %s (from %s)' % (utils.quoted(name),
msg.prefix))
irc.replySuccess()
except AliasError, e:
irc.error(str(e))

View File

@ -103,7 +103,8 @@ class Anonymous(callbacks.Privmsg):
"""
(channel, text) = privmsgs.getArgs(args, required=2)
self._preCheck(irc, msg, channel)
self.log.info('Saying %r in %s due to %s.', text, channel, msg.prefix)
self.log.info('Saying %s in %s due to %s.',
utils.quoted(text), channel, msg.prefix)
irc.queueMsg(ircmsgs.privmsg(channel, text))
def do(self, irc, msg, args):
@ -113,8 +114,8 @@ class Anonymous(callbacks.Privmsg):
"""
(channel, action) = privmsgs.getArgs(args, required=2)
self._preCheck(irc, msg, channel)
self.log.info('Performing %r in %s due to %s.',
action, channel, msg.prefix)
self.log.info('Performing %s in %s due to %s.',
utils.quoted(action), channel, msg.prefix)
irc.queueMsg(ircmsgs.action(channel, action))

View File

@ -135,7 +135,8 @@ class Bayes(callbacks.Privmsg):
(kind, prob) = kind
prob *= 100
text = utils.ellipsisify(text, 30)
self.log.debug('Classified %r as %s. (%.2f%%)', text, kind, prob)
self.log.debug('Classified %s as %s. (%.2f%%)',
utils.quoted(text), kind, prob)
self.db.trainNick(channel, msg.nick, text)
def guess(self, irc, msg, args, channel, text):

View File

@ -89,7 +89,7 @@ conf.registerChannelValue(conf.supybot.plugins.Bugzilla, 'bugSnarfer',
conf.registerChannelValue(conf.supybot.plugins.Bugzilla, 'bold',
registry.Boolean(True, """Determines whether results are bolded."""))
conf.registerChannelValue(conf.supybot.plugins.Bugzilla, 'replyNoBugzilla',
registry.String('I don\'t have a bugzilla %r.', """Determines the phrase
registry.String('I don\'t have a bugzilla %s.', """Determines the phrase
to use when notifying the user that there is no information about that
bugzilla site."""))
conf.registerChannelValue(conf.supybot.plugins.Bugzilla, 'snarfTarget',
@ -333,8 +333,8 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp):
if not bugs:
irc.error('I could not find any bugs.')
return
s = '%s match %r (%s): %s.' % \
(utils.nItems('bug', len(bugs)), searchstr,
s = '%s match %s (%s): %s.' % \
(utils.nItems('bug', len(bugs)), utils.quoted(searchstr),
' AND '.join(keywords), utils.commaAndify(map(str, bugids)))
irc.reply(s)
search = wrap(search, [getopts({'keywords':'text'}), 'text', 'text'])
@ -349,7 +349,7 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp):
(url, description) = self.db[name]
except KeyError:
s = self.registryValue('replyNoBugzilla', msg.args[0])
irc.error(s % name)
irc.error(s % utils.quoted(name))
return
queryurl = '%s/xml.cgi?id=%s' % (url, number)
try:

View File

@ -164,8 +164,9 @@ class Ctcp(callbacks.PrivmsgCommandAndRegexp):
L = []
for (reply, nicks) in self.versions.iteritems():
if nicks:
L.append('%s responded with %r' %
(utils.commaAndify(nicks), reply))
L.append('%s responded with %s' %
(utils.commaAndify(nicks),
utils.quoted(reply)))
else:
L.append(reply)
irc.reply(utils.commaAndify(L))

View File

@ -130,10 +130,11 @@ class Dict(callbacks.Privmsg):
dbs = sets.Set()
if not definitions:
if dictionary == '*':
irc.reply('No definition for %r could be found.' % word)
irc.reply('No definition for %s could be found.' %
utils.quoted(word))
else:
irc.reply('No definition for %r could be found in %s' %
(word, ircutils.bold(dictionary)))
irc.reply('No definition for %s could be found in %s' %
(utils.quoted(word), ircutils.bold(dictionary)))
return
L = []
for d in definitions:

View File

@ -162,8 +162,8 @@ class Dunno(callbacks.Privmsg):
return text.lower() in dunno.text.lower()
ids = [str(dunno.id) for dunno in self.db.select(channel, p)]
if ids:
s = 'Dunno search for %r (%s found): %s.' % \
(text, len(ids), utils.commaAndify(ids))
s = 'Dunno search for %s (%s found): %s.' % \
(utils.quoted(text), len(ids), utils.commaAndify(ids))
irc.reply(s)
else:
irc.reply('No dunnos found matching that search criteria.')
@ -180,8 +180,8 @@ class Dunno(callbacks.Privmsg):
name = ircdb.users.getUser(dunno.by).name
at = time.localtime(dunno.at)
timeStr = time.strftime(conf.supybot.humanTimestampFormat(), at)
irc.reply("Dunno #%s: %r (added by %s at %s)" % \
(id, dunno.text, name, timeStr))
irc.reply("Dunno #%s: %s (added by %s at %s)" % \
(id, utils.quoted(dunno.text), name, timeStr))
except KeyError:
irc.error('No dunno found with that id.')
get = wrap(get, ['channeldb', ('id', 'dunno')])

View File

@ -34,8 +34,6 @@ available on demand via several commands.
__revision__ = "$Id$"
import supybot.plugins as plugins
import time
import getopt
import string
@ -45,6 +43,8 @@ from itertools import imap
import supybot.conf as conf
import supybot.utils as utils
import supybot.ircdb as ircdb
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.privmsgs as privmsgs
import supybot.registry as registry
@ -79,19 +79,36 @@ conf.registerChannelValue(conf.supybot.plugins.Factoids,
'factoidPrefix', registry.StringWithSpaceOnRight('could be ', """Determines
the string that factoids will be introduced by."""))
class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
def __init__(self):
callbacks.Privmsg.__init__(self)
plugins.ChannelDBHandler.__init__(self)
class MultiKeyError(KeyError):
pass
def die(self):
callbacks.Privmsg.die(self)
plugins.ChannelDBHandler.die(self)
class LockError(Exception):
pass
def makeDb(self, filename):
class SqliteFactoidsDB(object):
def __init__(self, filename):
self.dbs = ircutils.IrcDict()
self.filename = filename
def close(self):
for db in self.dbs.itervalues():
db.close()
def _getDb(self, channel):
try:
import sqlite
except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to ' \
'use this plugin. Download it at ' \
'<http://pysqlite.sf.net/>'
filename = plugins.makeChannelFilename(self.filename, channel)
if filename in self.dbs:
return self.dbs[filename]
if os.path.exists(filename):
return sqlite.connect(filename)
self.dbs[filename] = sqlite.connect(filename)
return self.dbs[filename]
db = sqlite.connect(filename)
self.dbs[filename] = db
cursor = db.cursor()
cursor.execute("""CREATE TABLE keys (
id INTEGER PRIMARY KEY,
@ -114,25 +131,8 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
db.commit()
return db
def learn(self, irc, msg, args):
"""[<channel>] <key> as <value>
Associates <key> with <value>. <channel> is only necessary if the
message isn't sent on the channel itself. The word 'as' is necessary
to separate the key from the value. It can be changed to another
word via the learnSeparator registry value.
"""
channel = privmsgs.getChannel(msg, args)
try:
separator = conf.supybot.plugins.Factoids. \
learnSeparator.get(channel)()
i = args.index(separator)
except ValueError:
raise callbacks.ArgumentError
args.pop(i)
key = ' '.join(args[:i])
factoid = ' '.join(args[i:])
db = self.getDb(channel)
def add(self, channel, key, factoid, name):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s", key)
if cursor.rowcount == 0:
@ -140,21 +140,15 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
db.commit()
cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s",key)
(id, locked) = imap(int, cursor.fetchone())
capability = ircdb.makeChannelCapability(channel, 'factoids')
if not locked:
if ircdb.users.hasUser(msg.prefix):
name = ircdb.users.getUser(msg.prefix).name
else:
name = msg.nick
cursor.execute("""INSERT INTO factoids VALUES
(NULL, %s, %s, %s, %s)""",
id, name, int(time.time()), factoid)
db.commit()
irc.replySuccess()
else:
irc.error('That factoid is locked.')
raise LockError
def _lookupFactoid(self, channel, key):
def get(self, channel, key):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT factoids.fact FROM factoids, keys
@ -163,17 +157,158 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
LIMIT 20""", key)
return [t[0] for t in cursor.fetchall()]
def lock(self, channel, key):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("UPDATE keys SET locked=1 WHERE key LIKE %s", key)
db.commit()
def unlock(self, channel, key):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("UPDATE keys SET locked=0 WHERE key LIKE %s", key)
db.commit()
def remove(self, channel, key, number):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT keys.id, factoids.id
FROM keys, factoids
WHERE key LIKE %s AND
factoids.key_id=keys.id""", key)
if cursor.rowcount == 0:
raise dbi.NoRecordError
elif cursor.rowcount == 1 or number is True:
(id, _) = cursor.fetchone()
cursor.execute("""DELETE FROM factoids WHERE key_id=%s""", id)
cursor.execute("""DELETE FROM keys WHERE key LIKE %s""", key)
db.commit()
else:
if number is not None:
results = cursor.fetchall()
try:
(_, id) = results[number]
except IndexError:
raise dbi.NoRecordError
cursor.execute("DELETE FROM factoids WHERE id=%s", id)
db.commit()
else:
raise MultiKeyError, cursor.rowcount
def random(self, channel):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT fact, key_id FROM factoids
ORDER BY random()
LIMIT 3""")
if cursor.rowcount == 0:
raise dbi.NoRecordError
L = []
for (factoid, id) in cursor.fetchall():
cursor.execute("""SELECT key FROM keys WHERE id=%s""", id)
(key,) = cursor.fetchone()
L.append((key, factoid))
return L
def info(self, channel, key):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s", key)
if cursor.rowcount == 0:
raise dbi.NoRecordError
(id, locked) = imap(int, cursor.fetchone())
cursor.execute("""SELECT added_by, added_at FROM factoids
WHERE key_id=%s
ORDER BY id""", id)
return (locked, cursor.fetchall())
_sqlTrans = string.maketrans('*?', '%_')
def select(self, channel, values, predicates, globs):
db = self.getDb(channel)
cursor = db.cursor()
tables = ['keys']
criteria = []
predicateName = 'p'
formats = []
if not values:
target = 'keys.key'
else:
target = 'factoids.fact'
if 'factoids' not in tables:
tables.append('factoids')
criteria.append('factoids.key_id=keys.id')
for glob in globs:
criteria.append('TARGET LIKE %s')
formats.append(glob.translate(self._sqlTrans))
for predicate in predicates:
criteria.append('%s(TARGET)' % predicateName)
def p(s, r=arg):
return int(bool(predicate(s)))
db.create_function(predicateName, 1, p)
predicateName += 'p'
sql = """SELECT keys.key FROM %s WHERE %s""" % \
(', '.join(tables), ' AND '.join(criteria))
sql = sql.replace('TARGET', target)
cursor.execute(sql, formats)
if cursor.rowcount == 0:
raise dbi.NoRecordError
elif cursor.rowcount == 1 and \
conf.supybot.plugins.Factoids.showFactoidIfOnlyOneMatch.get(channel)():
return cursor.fetchone()[0]
elif cursor.rowcount > 100:
return None
else:
return cursor.fetchall()
class Factoids(callbacks.Privmsg):
def __init__(self):
self.__parent = super(Factoids, self)
self.__parent.__init__()
self.db = FactoidsDB()
def die(self):
self.__parent.die()
self.db.close()
def learn(self, irc, msg, args, channel, text):
"""[<channel>] <key> as <value>
Associates <key> with <value>. <channel> is only necessary if the
message isn't sent on the channel itself. The word 'as' is necessary
to separate the key from the value. It can be changed to another
word via the learnSeparator registry value.
"""
try:
separator = conf.supybot.plugins.Factoids. \
learnSeparator.get(channel)()
i = text.index(separator)
except ValueError:
raise callbacks.ArgumentError
text.pop(i)
key = ' '.join(text[:i])
factoid = ' '.join(text[i:])
try:
name = ircdb.users.getUser(msg.prefix).name
except KeyError:
name = msg.nick
try:
self.db.add(channel, key, factoid, nick)
irc.replySuccess()
except LockError:
irc.error('That factoid is locked.')
learn = wrap(learn, ['channeldb', many('text')])
def _replyFactoids(self, irc, channel, key, factoids, number=0, error=True):
if factoids:
if number:
try:
irc.reply(factoids[number-1])
irc.reply(factoids[number])
except IndexError:
irc.error('That\'s not a valid number for that key.')
return
else:
intro = self.registryValue('factoidPrefix', channel)
prefix = '%r %s' % (key, intro)
prefix = '%s %s' % (utils.quoted(key), intro)
if len(factoids) == 1:
irc.reply(prefix + factoids[0])
else:
@ -192,64 +327,44 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
channel = msg.args[0]
if self.registryValue('replyWhenInvalidCommand', channel):
key = ' '.join(tokens)
factoids = self._lookupFactoid(channel, key)
factoids = self.db.get(channel, key)
self._replyFactoids(irc, channel, key, factoids, error=False)
def whatis(self, irc, msg, args):
def whatis(self, irc, msg, args, channel, number, key):
"""[<channel>] <key> [<number>]
Looks up the value of <key> in the factoid database. If given a
number, will return only that exact factoid. <channel> is only
necessary if the message isn't sent in the channel itself.
"""
channel = privmsgs.getChannel(msg, args)
if len(args) > 1 and args[-1].isdigit():
number = args.pop()
else:
number = ''
key = privmsgs.getArgs(args)
if number:
try:
number = int(number)
except ValueError:
irc.error('%s is not a valid number.' % number)
return
else:
number = 0
factoids = self._lookupFactoid(channel, key)
factoids = self.db.get(channel, key)
self._replyFactoids(irc, channel, key, factoids, number)
whatis = wrap(whatis, ['channeldb', reversed(optional('positiveInt', 0)), 'text'],
allowExtra=True)
def lock(self, irc, msg, args):
def lock(self, irc, msg, args, channel, key):
"""[<channel>] <key>
Locks the factoid(s) associated with <key> so that they cannot be
removed or added to. <channel> is only necessary if the message isn't
sent in the channel itself.
"""
channel = privmsgs.getChannel(msg, args)
key = privmsgs.getArgs(args)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("UPDATE keys SET locked=1 WHERE key LIKE %s", key)
db.commit()
self.db.lock(channel, key)
irc.replySuccess()
lock = wrap(lock, ['channeldb', 'text'], allowExtra=True)
def unlock(self, irc, msg, args):
def unlock(self, irc, msg, args, channel, key):
"""[<channel>] <key>
Unlocks the factoid(s) associated with <key> so that they can be
removed or added to. <channel> is only necessary if the message isn't
sent in the channel itself.
"""
channel = privmsgs.getChannel(msg, args)
key = privmsgs.getArgs(args)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("UPDATE keys SET locked=0 WHERE key LIKE %s", key)
db.commit()
self.db.unlock(channel, key)
irc.replySuccess()
unlock = wrap(unlock, ['channeldb', 'text'], allowExtra=True)
def forget(self, irc, msg, args):
def forget(self, irc, msg, args, channel, number, key):
"""[<channel>] <key> [<number>|*]
Removes the factoid <key> from the factoids database. If there are
@ -258,92 +373,49 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
factoids associated with a key. <channel> is only necessary if
the message isn't sent in the channel itself.
"""
channel = privmsgs.getChannel(msg, args)
if args[-1].isdigit():
number = int(args.pop())
number -= 1
if number < 0:
irc.error('Negative numbers aren\'t valid.')
return
elif args[-1] == '*':
del args[-1]
if number == '*':
number = True
else:
number = None
key = privmsgs.getArgs(args)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT keys.id, factoids.id
FROM keys, factoids
WHERE key LIKE %s AND
factoids.key_id=keys.id""", key)
if cursor.rowcount == 0:
irc.error('There is no such factoid.')
elif cursor.rowcount == 1 or number is True:
(id, _) = cursor.fetchone()
cursor.execute("""DELETE FROM factoids WHERE key_id=%s""", id)
cursor.execute("""DELETE FROM keys WHERE key LIKE %s""", key)
db.commit()
irc.replySuccess()
else:
if number is not None:
results = cursor.fetchall()
number -= 1
key = ' '.join(key)
try:
(_, id) = results[number]
except IndexError:
irc.error('Invalid factoid number.')
return
cursor.execute("DELETE FROM factoids WHERE id=%s", id)
db.commit()
self.db.remove(channel, key, number)
irc.replySuccess()
else:
except dbi.NoRecordError:
irc.error('There is no such factoid.')
except MultiKeyError, e:
irc.error('%s factoids have that key. '
'Please specify which one to remove, '
'or use * to designate all of them.' %
cursor.rowcount)
'or use * to designate all of them.' % str(e))
forget = wrap(forget, ['channeldb',
reversed(first('positiveInt', ('literal', '*'))),
'text'], allowExtra=True)
def random(self, irc, msg, args):
def random(self, irc, msg, args, channel):
"""[<channel>]
Returns a random factoid from the database for <channel>. <channel>
is only necessary if the message isn't sent in the channel itself.
"""
channel = privmsgs.getChannel(msg, args)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT fact, key_id FROM factoids
ORDER BY random()
LIMIT 3""")
if cursor.rowcount != 0:
L = []
for (factoid, id) in cursor.fetchall():
cursor.execute("""SELECT key FROM keys WHERE id=%s""", id)
(key,) = cursor.fetchone()
L.append('"%s": %s' % (ircutils.bold(key), factoid))
try:
L = ['"%s": %s' % (ircutils.bold(k), v)
for (k, v) in self.db.random(channel)]
irc.reply('; '.join(L))
else:
except dbi.NoRecordError:
irc.error('I couldn\'t find a factoid.')
random = wrap(random, ['channeldb'])
def info(self, irc, msg, args):
def info(self, irc, msg, args, channel, key):
"""[<channel>] <key>
Gives information about the factoid(s) associated with <key>.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
channel = privmsgs.getChannel(msg, args)
key = privmsgs.getArgs(args)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s", key)
if cursor.rowcount == 0:
irc.error('No factoid matches that key.')
return
(id, locked) = imap(int, cursor.fetchone())
cursor.execute("""SELECT added_by, added_at FROM factoids
WHERE key_id=%s
ORDER BY id""", id)
factoids = cursor.fetchall()
try:
(locked, factoids) = self.db.info(channel, key)
except dbi.NoRecordError:
irc.error('No factoid matches that key.', Raise=True)
L = []
counter = 0
for (added_by, added_at) in factoids:
@ -352,10 +424,11 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
time.localtime(int(added_at)))
L.append('#%s was added by %s at %s' % (counter,added_by,added_at))
factoids = '; '.join(L)
s = 'Key %r is %s and has %s associated with it: %s' % \
(key, locked and 'locked' or 'not locked',
s = 'Key %s is %s and has %s associated with it: %s' % \
(utils.quoted(key), locked and 'locked' or 'not locked',
utils.nItems('factoid', counter), factoids)
irc.reply(s)
info = wrap(info, ['channeldb', 'text'], allowExtra=True)
def change(self, irc, msg, args):
"""[<channel>] <key> <number> <regexp>
@ -384,7 +457,7 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
WHERE keys.key LIKE %s AND
keys.id=factoids.key_id""", key)
if cursor.rowcount == 0:
irc.error('I couldn\'t find any key %r' % key)
irc.error('I couldn\'t find any key %s' % utils.quoted(key))
return
elif cursor.rowcount < number:
irc.error('That\'s not a valid key id.')
@ -395,63 +468,42 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
db.commit()
irc.replySuccess()
_sqlTrans = string.maketrans('*?', '%_')
def search(self, irc, msg, args):
def search(self, irc, msg, args, channel, optlist, globs):
"""[<channel>] [--values] [--{regexp}=<value>] [<glob>]
Searches the keyspace for keys matching <glob>. If --regexp is given,
it associated value is taken as a regexp and matched against the keys.
If --values is given, search the value space instead of the keyspace.
"""
channel = privmsgs.getChannel(msg, args)
(optlist, rest) = getopt.getopt(args, '', ['values', 'regexp='])
if not optlist and not rest:
if not optlist and not globs:
raise callbacks.ArgumentError
tables = ['keys']
formats = []
criteria = []
target = 'keys.key'
predicateName = 'p'
db = self.getDb(channel)
values = False
for (option, arg) in optlist:
if option == '--values':
target = 'factoids.fact'
if 'factoids' not in tables:
tables.append('factoids')
criteria.append('factoids.key_id=keys.id')
elif option == '--regexp':
criteria.append('%s(TARGET)' % predicateName)
try:
r = utils.perlReToPythonRe(arg)
except ValueError, e:
irc.error('Invalid regexp: %s' % e)
return
def p(s, r=r):
return int(bool(r.search(s)))
db.create_function(predicateName, 1, p)
predicateName += 'p'
for glob in rest:
if option == 'values':
values = True
elif option == 'regexp':
predicates.append(r.search)
L = []
for glob in globs:
if '*' not in glob and '?' not in glob:
glob = '*%s*' % glob
criteria.append('TARGET LIKE %s')
formats.append(glob.translate(self._sqlTrans))
cursor = db.cursor()
sql = """SELECT keys.key FROM %s WHERE %s""" % \
(', '.join(tables), ' AND '.join(criteria))
sql = sql.replace('TARGET', target)
cursor.execute(sql, formats)
if cursor.rowcount == 0:
irc.reply('No keys matched that query.')
elif cursor.rowcount == 1 and \
conf.supybot.plugins.Factoids.showFactoidIfOnlyOneMatch.get(channel)():
self.whatis(irc, msg, [cursor.fetchone()[0]])
elif cursor.rowcount > 100:
L.append(glob)
try:
factoids = self.db.select(channel, values, predicates, L)
if isinstance(factoids, basestring):
self.whatis(irc, msg, factoids)
elif factoids is None:
irc.reply('More than 100 keys matched that query; '
'please narrow your query.')
else:
keys = [repr(t[0]) for t in cursor.fetchall()]
keys = [repr(t[0]) for t in factoids]
s = utils.commaAndify(keys)
irc.reply(s)
except dbi.NoRecordError:
irc.reply('No keys matched that query.')
search = wrap(search, ['channeldb',
getopts({'values':'', 'regexp':'regexpMatcher'}),
additional('text')])
Class = Factoids

View File

@ -144,8 +144,8 @@ class FunDB(callbacks.Privmsg):
lowerTypes = [t.lower() for t in self._types]
if type.lower() not in lowerTypes:
if error:
irc.error('%r is not a valid type. Valid types include %s.' %
(type, utils.commaAndify(self._types)))
irc.error('%s is not a valid type. Valid types include %s.' %
(utils.quoted(type), utils.commaAndify(self._types)))
return False
else:
return True
@ -315,8 +315,8 @@ class FunDB(callbacks.Privmsg):
return
try:
x = self.db.get(channel, type, id)
reply = '%s #%s: %r; Created by %s.' % (type, x.id, x.text,
self._getBy(x.by))
reply = '%s #%s: %s; Created by %s.' % \
(type, x.id, utils.quoted(x.text), self._getBy(x.by))
irc.reply(reply)
except KeyError:
irc.error('There is no %s with that id.' % type)

View File

@ -340,8 +340,8 @@ class Google(callbacks.PrivmsgCommandAndRegexp):
categories = utils.commaAndify(categories)
else:
categories = ''
s = 'Search for %r returned %s %s results in %s seconds.%s' % \
(meta.searchQuery,
s = 'Search for %s returned %s %s results in %s seconds.%s' % \
(utils.quoted(meta.searchQuery),
meta.estimateIsExact and 'exactly' or 'approximately',
meta.estimatedTotalResultsCount,
meta.searchTime,

View File

@ -618,10 +618,12 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
if isAre in ('was', 'is', 'am'):
if self.db.hasIs(key):
if also:
self.log.debug('Adding %r to %r.', key, value)
self.log.debug('Adding %s to %s.',
utils.quoted(key), utils.quoted(value))
value = '%s or %s' % (self.db.getIs(key), value)
elif self.force:
self.log.debug('Forcing %r to %r.', key, value)
self.log.debug('Forcing %s to %s.',
utils.quoted(key), utils.quoted(value))
elif self.badForce:
value = self.db.getIs(key)
self.reply('... but %s is %s, %s ...' % (key, value,
@ -632,16 +634,19 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
self.reply('But %s is %s, %s.' % (key, value, msg.nick))
return
else:
self.log.debug('Already have a %r key.', key)
self.log.debug('Already have a %s key.',
utils.quoted(key))
return
self.db.setIs(key, value)
else:
if self.db.hasAre(key):
if also:
self.log.debug('Adding %r to %r.', key, value)
self.log.debug('Adding %s to %s.',
utils.quoted(key), utils.quoted(value))
value = '%s or %s' % (self.db.getAre(key), value)
elif self.force:
self.log.debug('Forcing %r to %r.', key, value)
self.log.debug('Forcing %s to %s.',
utils.quoted(key), utils.quoted(value))
elif self.badForce:
value = self.db.getAre(key)
self.reply('... but %s are %s, %s ...' % (key, value,
@ -652,7 +657,8 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
self.reply('But %s are %s, %s.' % (key, value, msg.nick))
return
else:
self.log.debug('Already have a %r key.', key)
self.log.debug('Already have a %s key.',
utils.quoted(key))
return
self.db.setAre(key, value)
if self.addressed or self.force:

View File

@ -297,9 +297,9 @@ class Karma(callbacks.Privmsg):
if self.registryValue('simpleOutput', channel):
s = '%s: %s' % (name, total)
else:
s = 'Karma for %r has been increased %s ' \
s = 'Karma for %s has been increased %s ' \
'and decreased %s for a total karma of %s.' % \
(name, utils.nItems('time', added),
(utils.quoted(name), utils.nItems('time', added),
utils.nItems('time', subtracted), total)
irc.reply(s)
elif len(args) > 1:
@ -317,8 +317,10 @@ class Karma(callbacks.Privmsg):
else: # No name was given. Return the top/bottom N karmas.
limit = self.registryValue('rankingDisplay', channel)
top = self.db.top(channel, limit)
highest = ['%r (%s)' % t for t in self.db.top(channel, limit)]
lowest = ['%r (%s)' % t for t in self.db.bottom(channel, limit)]
highest = ['%s (%s)' % (utils.quoted(s), t)
for (s, t) in self.db.top(channel, limit)]
lowest = ['%s (%s)' % (utils.quoted(s), t)
for (s, t) in self.db.bottom(channel, limit)]
if not (highest and lowest):
irc.error('I have no karma for this channel.')
return
@ -348,7 +350,7 @@ class Karma(callbacks.Privmsg):
L = self.db.most(channel, kind,
self.registryValue('mostDisplay', channel))
if L:
L = ['%r: %s' % (name, i) for (name, i) in L]
L = ['%s: %s' % (utils.quoted(name), i) for (name, i) in L]
irc.reply(utils.commaAndify(L))
else:
irc.error('I have no karma for this channel.')

View File

@ -99,7 +99,7 @@ class IrcHandler(logging.Handler):
# baaaaaad.
irc.sendMsg(msg)
else:
print '*** Not sending to %r' % target
print '*** Not sending to %s' % utils.quoted(target)
class IrcFormatter(log.Formatter):

View File

@ -146,7 +146,7 @@ class SqliteLookupDB(object):
key = key.replace('\\:', ':')
except ValueError:
cursor.execute("""DROP TABLE %s""" % name)
s = 'Invalid line in %s: %r' % (filename, line)
s = 'Invalid line in %s: %s' % (filename, utils.quoted(line))
raise callbacks.Error, s
cursor.execute(sql, key, value)
cursor.execute("CREATE INDEX %s_keys ON %s (key)" % (name, name))
@ -191,8 +191,7 @@ class SqliteLookupDB(object):
try:
r = utils.perlReToPythonRe(arg)
except ValueError, e:
irc.error('%r is not a valid regular expression' %
arg)
irc.errorInvalid('regular expression' % arg)
return
def p(s, r=r):
return int(bool(r.search(s)))

View File

@ -193,7 +193,8 @@ class Math(callbacks.Privmsg):
return str(x)
text = self._mathRe.sub(handleMatch, text)
try:
self.log.info('evaluating %r from %s' % (text, msg.prefix))
self.log.info('evaluating %s from %s' %
(utils.quoted(text), msg.prefix))
x = complex(eval(text, self._mathEnv, self._mathEnv))
irc.reply(self._complexToString(x))
except OverflowError:
@ -227,7 +228,8 @@ class Math(callbacks.Privmsg):
return
text = text.replace('lambda', '')
try:
self.log.info('evaluating %r from %s' % (text, msg.prefix))
self.log.info('evaluating %s from %s' %
(utils.quoted(text), msg.prefix))
irc.reply(str(eval(text, self._mathEnv, self._mathEnv)))
except OverflowError:
maxFloat = math.ldexp(0.9999999999999999, 1024)
@ -283,7 +285,8 @@ class Math(callbacks.Privmsg):
try:
stack.append(eval(s, self._mathEnv, self._mathEnv))
except SyntaxError:
irc.error('%r is not a defined function.' % arg)
irc.error('%s is not a defined function.' %
utils.quoted(arg))
return
if len(stack) == 1:
irc.reply(str(self._complexToString(complex(stack[0]))))

View File

@ -398,7 +398,8 @@ class MoobotFactoids(callbacks.Privmsg):
elif 'is' in tokens:
p = 'is'.__eq__
else:
s = 'Invalid tokens for {add,change}Factoid: %r' % tokens
s = 'Invalid tokens for {add,change}Factoid: %s' % \
utils.quoted(tokens)
raise ValueError, s
(key, newfact) = map(' '.join, utils.itersplit(p, tokens, maxsplit=1))
key = self._sanitizeKey(key)

View File

@ -366,7 +366,8 @@ class Note(callbacks.Privmsg):
try:
L.remove(user)
except (KeyError, ValueError):
irc.error('%r was not in your list of ignores.' % user)
irc.error('%s was not in your list of ignores.' %
utils.quoted(user))
return
else:
L.add(user)

View File

@ -133,9 +133,11 @@ class Poll(callbacks.Privmsg, plugins.ChannelDBHandler):
else:
options = cursor.fetchall()
optionstr = 'Options: %s' % \
' '.join(['%s: %r' % tuple(t) for t in options])
pollstr = 'Poll #%s: %r started by %s. %s. Poll is %s.' % \
(poll_id, question, starter, optionstr, statusstr)
' '.join(['%s: %s' % (s, utils.quoted(t)) \
for (s, t) in options])
pollstr = 'Poll #%s: %s started by %s. %s. Poll is %s.' % \
(poll_id, utils.quoted(question), starter, optionstr,
statusstr)
irc.reply(pollstr)
def open(self, irc, msg, args):
@ -304,7 +306,7 @@ class Poll(callbacks.Privmsg, plugins.ChannelDBHandler):
WHERE id=%s AND poll_id=%s""",
option_id, poll_id)
option = cursor.fetchone()[0]
results.append('%r: %s' % (option, int(count)))
results.append('%s: %s' % (utils.quoted(option), int(count)))
s = utils.commaAndify(results)
reply += ' - %s' % s
irc.reply(reply)
@ -322,7 +324,8 @@ class Poll(callbacks.Privmsg, plugins.ChannelDBHandler):
if cursor.rowcount == 0:
irc.reply('This channel currently has no open polls.')
else:
polls = ['#%s: %r' % tuple(t) for t in cursor.fetchall()]
polls = ['#%s: %s' % (s, utils.quoted(t))
for (s, t) in cursor.fetchall()]
irc.reply(utils.commaAndify(polls))
Class = Poll

View File

@ -258,7 +258,7 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
cursor.execute("""SELECT quote, hostmask, added_at, added_by
FROM quotegrabs WHERE id = %s""", id)
if cursor.rowcount == 0:
irc.error('No quotegrab for id %r' % id)
irc.error('No quotegrab for id %s' % utils.quoted(id))
return
quote, hostmask, timestamp, grabber_mask = cursor.fetchone()
time_str = time.strftime(conf.supybot.humanTimestampFormat(),

View File

@ -67,8 +67,8 @@ class QuoteRecord(dbi.Record):
user = self.by
except KeyError:
user = 'a user that is no longer registered'
return 'Quote %r added by %s at %s.' % \
(self.text, user,
return 'Quote %s added by %s at %s.' % \
(utils.quoted(self.text), user,
time.strftime(format, time.localtime(float(self.at))))
class SqliteQuotesDB(object):
@ -258,7 +258,8 @@ class Quotes(callbacks.Privmsg):
irc.reply('More than 10 quotes matched your criteria. '
'Please narrow your query.')
else:
quotes = ['#%s: %r' % (q.id, utils.ellipsisify(q.text, 30))
quotes = ['#%s: %s' %
(q.id, utils.quoted(utils.ellipsisify(q.text, 30)))
for q in quote]
irc.reply(utils.commaAndify(quotes))
@ -331,7 +332,7 @@ class Quotes(callbacks.Privmsg):
try:
id = int(id)
except ValueError:
irc.error('Invalid id: %r' % id)
irc.errorInvalid('id' % id)
return
try:
quote = self.db.get(channel, id)
@ -350,7 +351,7 @@ class Quotes(callbacks.Privmsg):
try:
id = int(id)
except ValueError:
irc.error('That\'s not a valid id: %r' % id)
irc.errorInvalid('id' % id)
try:
self.db.remove(channel, id)
irc.replySuccess()

View File

@ -304,9 +304,8 @@ class RSS(callbacks.Privmsg):
try:
name = self._validFeedName(name)
except ValueError:
irc.error('%r is not a valid feed name. Feed names must not '
'include dots, colons, or spaces.' % name)
return
irc.errorInvalid('feed name', name, 'Feed names must not '
'include dots, colons, or spaces.')
self.makeFeedCommand(name, url)
irc.replySuccess()

View File

@ -380,7 +380,8 @@ class Services(privmsgs.CapabilityCheckingPrivmsg):
'NickServ. Check email at %s and send the auth '
'command to NickServ.', email)
else:
self.log.debug('Unexpected notice from NickServ: %r.', s)
self.log.debug('Unexpected notice from NickServ: %s.',
utils.quoted(s))
def checkPrivileges(self, irc, channel):
chanserv = self.registryValue('ChanServ')

View File

@ -170,7 +170,8 @@ class ShrinkUrl(callbacks.PrivmsgCommandAndRegexp):
url = match.group(0)
r = self.registryValue('nonSnarfingRegexp', channel)
if r and r.search(url) is not None:
self.log.debug('Matched nonSnarfingRegexp: %r', url)
self.log.debug('Matched nonSnarfingRegexp: %s',
utils.quoted(url))
return
minlen = self.registryValue('minimumLength', channel)
cmd = self.registryValue('default', channel)
@ -181,7 +182,8 @@ class ShrinkUrl(callbacks.PrivmsgCommandAndRegexp):
elif cmd == 'ln':
(shorturl, _) = self._getLnUrl(url)
if shorturl is None:
self.log.info('Couldn\'t get shorturl for %r', url)
self.log.info('Couldn\'t get shorturl for %s',
utils.quoted(url))
return
domain = webutils.getDomain(url)
s = '%s (at %s)' % (ircutils.bold(shorturl), domain)

View File

@ -164,8 +164,8 @@ class Success(callbacks.Privmsg):
return text.lower() in success.text.lower()
ids = [str(success.id) for success in self.db.select(channel, p)]
if ids:
s = 'Success search for %r (%s found): %s.' % \
(text, len(ids), utils.commaAndify(ids))
s = 'Success search for %s (%s found): %s.' % \
(utils.quoted(text), len(ids), utils.commaAndify(ids))
irc.reply(s)
else:
irc.reply('No successes found matching that search criteria.')
@ -182,8 +182,8 @@ class Success(callbacks.Privmsg):
name = ircdb.users.getUser(success.by).name
at = time.localtime(success.at)
timeStr = time.strftime(conf.supybot.humanTimestampFormat(), at)
irc.reply("Success #%s: %r (added by %s at %s)" % \
(id, success.text, name, timeStr))
irc.reply("Success #%s: %s (added by %s at %s)" % \
(id, utils.quoted(success.text), name, timeStr))
except KeyError:
irc.error('No success found with that id.')
get = wrap(get, ['channeldb', ('id', 'success')])

View File

@ -110,8 +110,7 @@ class Todo(callbacks.Privmsg):
try:
userid = ircdb.users.getUserId(arg)
except KeyError:
irc.error(
'%r is not a valid task id or username' % arg)
irc.errorInvalid('task id or username', arg)
return
db = self.dbHandler.getDb()
cursor = db.cursor()
@ -149,7 +148,7 @@ class Todo(callbacks.Privmsg):
cursor.execute("""SELECT userid,priority,added_at,task,active
FROM todo WHERE id = %s""", taskid)
if cursor.rowcount == 0:
irc.error('%r is not a valid task id' % taskid)
irc.errorInvalid('task id', taskid)
return
(userid, pri, added_at, task, active) = cursor.fetchone()
# Construct and return the reply
@ -189,7 +188,7 @@ class Todo(callbacks.Privmsg):
try:
priority = int(arg)
except ValueError, e:
irc.error('%r is an invalid priority' % arg)
irc.errorInvalid('priority', arg)
return
text = privmsgs.getArgs(rest)
db = self.dbHandler.getDb()
@ -270,8 +269,7 @@ class Todo(callbacks.Privmsg):
try:
r = utils.perlReToPythonRe(arg)
except ValueError, e:
irc.error('%r is not a valid regular expression' %
arg)
irc.errorInvalid('regular expression', arg)
return
def p(s, r=r):
return int(bool(r.search(s)))
@ -335,7 +333,7 @@ class Todo(callbacks.Privmsg):
try:
replacer = utils.perlReToReplacer(regexp)
except ValueError:
irc.error('%r is not a valid regexp' % regexp)
irc.errorInvalid('regexp', regexp)
return
db = self.dbHandler.getDb()
cursor = db.cursor()
@ -343,7 +341,7 @@ class Todo(callbacks.Privmsg):
WHERE userid = %s AND id = %s
AND active = 1""", userid, taskid)
if cursor.rowcount == 0:
irc.error('%r is not a valid task id' % taskid)
irc.errorInvalid('task id', taskid)
return
newtext = replacer(cursor.fetchone()[0])
cursor.execute("""UPDATE todo SET task = %s

View File

@ -89,7 +89,7 @@ def canChangeTopic(irc, msg, args, state):
c = irc.state.channels[state.channel]
if irc.nick not in c.ops and 't' in c.modes:
irc.error('I can\'t change the topic, '
'I\'m not opped and %s is +t.' % channel, Raise=True)
'I\'m not opped and %s is +t.' % state.channel, Raise=True)
def getTopic(irc, msg, args, state, format=True):
separator = state.cb.registryValue('separator', state.channel)

View File

@ -108,9 +108,10 @@ class URL(callbacks.PrivmsgCommandAndRegexp):
for url in webutils.urlRe.findall(text):
r = self.registryValue('nonSnarfingRegexp', channel)
if r and r.search(url):
self.log.debug('Skipping adding %r to db.', url)
self.log.debug('Skipping adding %s to db.',
utils.quoted(url))
continue
self.log.debug('Adding %r to db.', url)
self.log.debug('Adding %s to db.', utils.quoted(url))
self.db.add(channel, url, msg)
self.__parent.doPrivmsg(irc, msg)
@ -125,7 +126,7 @@ class URL(callbacks.PrivmsgCommandAndRegexp):
url = match.group(0)
r = self.registryValue('nonSnarfingRegexp', channel)
if r and r.search(url):
self.log.debug('Not titleSnarfing %r.', url)
self.log.debug('Not titleSnarfing %s.', utils.quoted(url))
return
try:
size = conf.supybot.protocols.http.peekSize()

View File

@ -223,8 +223,8 @@ class Unix(callbacks.Privmsg):
resp = 'I could not find an alternate spelling for "%s"' % word
elif line[0] == '&':
matches = line.split(':')[1].strip()
resp = 'Possible spellings for %r: %s.' % \
(word, utils.commaAndify(matches.split(', ')))
resp = 'Possible spellings for %s: %s.' % \
(utils.quoted(word), utils.commaAndify(matches.split(', ')))
else:
resp = 'Something unexpected was seen in the [ai]spell output.'
irc.reply(resp)

View File

@ -234,8 +234,8 @@ class WordStats(callbacks.Privmsg):
self.db.delWord(channel, word)
irc.replySuccess()
else:
irc.error('%r doesn\'t look like a word I am keeping stats '
'on.' % word)
irc.error('%s doesn\'t look like a word I am keeping stats '
'on.' % utils.quoted(word))
return
else:
irc.error('I am not currently keeping any word stats.')
@ -277,14 +277,16 @@ class WordStats(callbacks.Privmsg):
try:
count = self.db.getWordCount(channel, id, word)
except KeyError:
irc.error('I\'m not keeping stats on %r.' % word)
irc.error('I\'m not keeping stats on %s.' %
utils.quoted(word))
return
if count:
s = '%s has said %r %s.' % \
(user, word, utils.nItems('time', count))
s = '%s has said %s %s.' % \
(user, utils.quoted(word), utils.nItems('time', count))
irc.reply(s)
else:
irc.error('%s has never said %r.' % (user, word))
irc.error('%s has never said %s.' %
(user, utils.quoted(word)))
elif arg1 in WordDict.fromkeys(self.db.getWords(channel)):
word = arg1
total = self.db.getTotalWordCount(channel, word)
@ -300,7 +302,7 @@ class WordStats(callbacks.Privmsg):
id = None
rank = None
number = None
ers = '%rer' % word
ers = '%ser' % utils.quoted(word)
L = []
for (userid, count) in self.db.getTopUsers(channel, word, n):
if userid == id:
@ -309,9 +311,7 @@ class WordStats(callbacks.Privmsg):
username = ircdb.users.getUser(userid).name
L.append('%s: %s' % (username, count))
except KeyError:
self.log.warning('Odd, I have a user in my WordStats '
'database that doesn\'t exist in my '
'user database: #%s.', userid)
L.append('%s: %s' % ('unregistered user', count))
ret = 'Top %s (out of a total of %s seen):' % \
(utils.nItems(ers, len(L)), utils.nItems(repr(word), total))
users = self.db.getNumUsers(channel)
@ -328,18 +328,19 @@ class WordStats(callbacks.Privmsg):
try:
id = ircdb.users.getUserId(user)
except KeyError:
irc.error('%r doesn\'t look like a word I\'m keeping stats '
'on or a user in my database.' % user)
irc.error('%s doesn\'t look like a word I\'m keeping stats '
'on or a user in my database.' % utils.quoted(user))
return
try:
L = ['%r: %s' % (word, count)
L = ['%s: %s' % (utils.quoted(word), count)
for (word,count) in self.db.getUserWordCounts(channel,id)]
if L:
L.sort()
irc.reply(utils.commaAndify(L))
else:
irc.error('%r doesn\'t look like a word I\'m keeping stats'
' on or a user in my database.' % user)
irc.error('%s doesn\'t look like a word I\'m keeping stats'
' on or a user in my database.' %
utils.quoted(user))
return
except KeyError:
irc.error('I have no word stats for that person.')

View File

@ -278,15 +278,18 @@ class Words(callbacks.Privmsg):
del game.unused[game.unused.index(letter)]
if letter in game.hidden:
self._hangmanReply(irc, channel,
'Yes, there is %s %r.' %
(game.letterArticle(letter), letter))
'Yes, there is %s %s.' %
(game.letterArticle(letter),
utils.quoted(letter)))
game.guess = game.addLetter(letter, game.guess,
game.letterPositions(letter,
game.hidden))
if game.guess == game.hidden:
game.guessed = True
else:
self._hangmanReply(irc, channel, 'No, there is no %r.' % letter)
self._hangmanReply(irc, channel,
'No, there is no %s.' %
utils.quoted(letter))
game.tries -= 1
self._hangmanReply(irc, channel,
'%s (%s left)' % (game.guess, game.triesLeft()))
@ -316,12 +319,13 @@ class Words(callbacks.Privmsg):
# Verify if the user won or lost
if game.guessed and game.tries > 0:
self._hangmanReply(irc, channel,
'You win! The word was indeed %r.' %
game.hidden)
'You win! The word was indeed %s.' %
utils.quoted(game.hidden))
self.endGame(channel)
elif not game.guessed and game.tries == 0:
self._hangmanReply(irc, channel,
'You lose! The word was %r.' % game.hidden)
'You lose! The word was %s.' %
utils.quoted(game.hidden))
self.endGame(channel)
guess = wrap(guess, ['channel', 'somethingWithoutSpaces'])
###

View File

@ -1181,6 +1181,11 @@ class SimpleProxy(RichReplyMethods):
self.msg = msg
def error(self, s, msg=None, **kwargs):
if 'Raise' in kwargs and kwargs['Raise']:
if s:
raise Error, s
else:
raise ArgumentError
if msg is None:
msg = self.msg
m = error(msg, s, **kwargs)

View File

@ -660,6 +660,11 @@ class first(context):
else:
raise e
class reversed(context):
def __call__(self, irc, msg, args, state):
args[:] = args[::-1]
super(reversed, self).__call__(irc, msg, args, state)
args[:] = args[::-1]
class getopts(context):
"""The empty string indicates that no argument is taken; None indicates
@ -775,7 +780,7 @@ def wrap(f, specList=[], **kw):
__all__ = ['wrap', 'context', 'additional', 'optional', 'any', 'compose',
'Spec', 'first', 'urlSnarfer', 'thread',
'Spec', 'first', 'urlSnarfer', 'thread', 'reversed',
'many', 'getopts', 'getConverter', 'addConverter', 'callConverter']
if world.testing:

View File

@ -77,7 +77,7 @@ class AliasTestCase(ChannelPluginTestCase, PluginDocumentation):
def testAliasHelp(self):
self.assertNotError('alias add slashdot foo')
self.assertRegexp('help slashdot', "Alias for 'foo.*'")
self.assertRegexp('help slashdot', "Alias for <<foo.*>>")
def testRemove(self):
self.assertNotError('alias add foo echo bar')

View File

@ -66,9 +66,8 @@ if sqlite is not None:
'Todos for tester: #1: wash my car and #2: moo')
# Check error
self.assertError('todo asfas')
self.assertResponse('todo asfas',
'Error: \'asfas\' is not a valid task id or '
'username')
self.assertRegexp('todo asfas',
'Error: \'asfas\' is not a valid task')
# Check priority sorting
self.assertNotError('todo setpriority 1 100')
self.assertNotError('todo setpriority 2 10')

View File

@ -69,6 +69,10 @@ class CommandsTestCase(SupyTestCase):
self.assertRaises(callbacks.Error,
self.assertState, spec, ['foo'], ['asdf'])
def testReversed(self):
spec = [reversed('positiveInt'), 'float', 'text']
self.assertState(spec, ['-1.0', 'foo', '1'], [1, -1.0, 'foo'])
def testGetopts(self):
spec = ['int', getopts({'foo': None, 'bar': 'int'}), 'int']
self.assertState(spec,