Swapped the argument order for utils.{pluralize,nItems}

This commit is contained in:
Jeremy Fincher 2003-12-12 15:41:33 +00:00
parent f4f91bcdb0
commit 42ce8c33a6
23 changed files with 149 additions and 133 deletions

View File

@ -1,14 +1,17 @@
* Added Lookup.search
* Added Lookup.search
* Updated Todo.remove to allow removing multiple taskids
* Updated Todo.remove to allow removing multiple taskids
* Added Topic.reorder, a new command for reordering the topics in a
* Added Topic.reorder, a new command for reordering the topics in a
specific manner.
* Added Http.extension, a new command to retrieve file extension in
* Added Topic.list, a new command for listing the topics in a
channel (mostly in order to help out with Topic.reorder :))
* Added Http.extension, a new command to retrieve file extension in
formation from filext.com.
* Updated Relay.whois to include a user's away status and identified
* Updated Relay.whois to include a user's away status and identified
state, if the server supports it.
2003-12-6 Jeremy Fincher <jemfinch@supybot.org>

View File

@ -151,7 +151,7 @@ def makeNewAlias(name, alias):
f = types.FunctionType(f.func_code, f.func_globals,
name, closure=f.func_closure)
f.__doc__ ='<an alias, %s>\n\nAlias for %r' % \
(utils.nItems(biggestDollar, 'argument'), alias)
(utils.nItems('argument', biggestDollar), alias)
return f

View File

@ -432,21 +432,21 @@ class ChannelDB(plugins.ChannelDBHandler,
'%s has joined %s, parted %s, quit %s, kicked someone %s, '\
'been kicked %s, changed the topic %s, ' \
'and changed the mode %s.' % \
(name, utils.nItems(values.msgs, 'message'),
utils.nItems(values.chars, 'character'),
utils.nItems(values.words, 'word'),
utils.nItems(values.smileys, 'smiley'),
utils.nItems(values.frowns, 'frown'),
(name, utils.nItems('message', values.msgs),
utils.nItems('character', values.chars),
utils.nItems('word', values.words),
utils.nItems('smiley', values.smileys),
utils.nItems('frown', values.frowns),
values.actions, values.actions == 1 and 'was an ACTION. '
or 'were ACTIONs. ',
name,
utils.nItems(values.joins, 'time'),
utils.nItems(values.parts, 'time'),
utils.nItems(values.quits, 'time'),
utils.nItems(values.kicks, 'time'),
utils.nItems(values.kicked, 'time'),
utils.nItems(values.topics, 'time'),
utils.nItems(values.modes, 'time'))
utils.nItems('time', values.joins),
utils.nItems('time', values.parts),
utils.nItems('time', values.quits),
utils.nItems('time', values.kicks),
utils.nItems('time', values.kicked),
utils.nItems('time', values.topics),
utils.nItems('time', values.modes))
irc.reply(msg, s)
def channelstats(self, irc, msg, args):
@ -538,7 +538,7 @@ class ChannelDB(plugins.ChannelDBHandler,
irc.error(msg, '%s has never said %r.' % (user, word))
return
count = int(cursor.fetchone()[0])
s = '%s has said %r %s.' % (user,word,utils.nItems(count, 'time'))
s = '%s has said %r %s.' % (user,word,utils.nItems('time', count))
irc.reply(msg, s)
else:
# Figure out if we got a user or a word
@ -594,9 +594,9 @@ class ChannelDB(plugins.ChannelDBHandler,
word)
total = int(cursor.fetchone()[0])
ers = '%rer' % word
ret = 'Top %s ' % utils.nItems(numResultsShown, ers)
ret = 'Top %s ' % utils.nItems(ers, numResultsShown)
ret += '(out of a total of %s seen):' % \
utils.nItems(total, repr(word))
utils.nItems(repr(word), total)
L = []
for (count, id) in results[:numResultsShown]:
username = ircdb.users.getUser(id).name
@ -607,7 +607,7 @@ class ChannelDB(plugins.ChannelDBHandler,
for (_, userId) in results:
if userId == id:
s = 'You are ranked %s out of %s.' % \
(rank, utils.nItems(len(results), ers))
(rank, utils.nItems(ers, len(results)))
break
else:
rank += 1

View File

@ -318,7 +318,7 @@ class Factoids(plugins.ChannelDBHandler, callbacks.Privmsg):
factoids = '; '.join(L)
s = 'Key %r is %s and has %s associated with it: %s' % \
(key, locked and 'locked' or 'not locked',
utils.nItems(counter, 'factoid'), factoids)
utils.nItems('factoid', counter), factoids)
irc.reply(msg, s)
def change(self, irc, msg, args):

View File

@ -259,7 +259,7 @@ class FunDB(callbacks.Privmsg, configurable.Mixin, plugins.ChannelDBHandler):
cursor.execute(sql)
total = int(cursor.fetchone()[0])
irc.reply(msg, 'There %s currently %s in my database.' %
(utils.be(total), utils.nItems(total, table)))
(utils.be(total), utils.nItems(table, total)))
def get(self, irc, msg, args):
"""[<channel>] <lart|excuse|insult|praise> <id>

View File

@ -140,18 +140,18 @@ class Gameknot(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin):
'and a record of %s, %s, and %s ' \
'(win/loss/draw percentage: %.2f%%/%.2f%%/%.2f%%). %s' % \
(name, team, rating, games,
utils.nItems(w, 'win'),
utils.nItems(l, 'loss'),
utils.nItems(d, 'draw'),
utils.nItems('win', w),
utils.nItems('loss', l),
utils.nItems('draw', d),
wp, lp, dp, seen)
else:
s = '%s is rated %s and has %s ' \
'and a record of %s, %s, and %s ' \
'(win/loss/draw percentage: %.2f%%/%.2f%%/%.2f%%). %s' % \
(name, rating, games,
utils.nItems(w, 'win'),
utils.nItems(l, 'loss'),
utils.nItems(d, 'draw'),
utils.nItems('win', w),
utils.nItems('loss', l),
utils.nItems('draw', d),
wp, lp, dp, seen)
return s
except AttributeError:

View File

@ -153,7 +153,7 @@ class Google(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin):
def formatData(self, data):
if isinstance(data, basestring):
return data
time = 'Search took %s seconds: ' % data.meta.searchTime
time = 'Search took %s seconds' % data.meta.searchTime
results = []
for result in data.results:
title = utils.htmlToText(result.title.encode('utf-8'))
@ -163,9 +163,9 @@ class Google(callbacks.PrivmsgCommandAndRegexp, configurable.Mixin):
else:
results.append(url)
if not results:
return 'No matches found %s' % time
return 'No matches found (%s)' % time
else:
return '%s %s' % (time, '; '.join(results))
return '%s: %s' % (time, '; '.join(results))
def licensekey(self, irc, msg, args):
"""<key>

View File

@ -124,11 +124,10 @@ class Karma(callbacks.PrivmsgCommandAndRegexp,
if self.configurables.get('simple-output', channel):
s = '%s: %s' % (name, total)
else:
s = 'Karma for %r has been increased %s %s ' \
'and decreased %s %s for a total karma of %s.' % \
(name, added, utils.pluralize(added, 'time'),
subtracted, utils.pluralize(subtracted, 'time'),
total)
s = 'Karma for %r has been increased %s ' \
'and decreased %s for a total karma of %s.' % \
(name, utils.nItems('time', added),
utils.nItems('time', subtracted), total)
irc.reply(msg, s)
elif len(args) > 1:
normalizedArgs = sets.Set(imap(str.lower, args))

View File

@ -435,7 +435,7 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
last_at = time.strftime(conf.humanTimestampFormat,
time.localtime(int(last_requested_at)))
req_count = requested_count
times_str = utils.nItems(requested_count, 'time')
times_str = utils.nItems('time', requested_count)
s += " Last requested by %s on %s, requested %s." % \
(last_by, last_at, times_str)
# Last, locked info
@ -538,14 +538,14 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
L = ['%s (%s)' % (ircdb.users.getUser(t[0]).name, int(t[1]))
for t in cursor.fetchall()]
return 'Most prolific %s: %s' % \
(utils.pluralize(len(L), 'author'), utils.commaAndify(L))
(utils.pluralize('author', len(L)), utils.commaAndify(L))
def _mostRecent(self, cursor, limit):
cursor.execute("""SELECT key FROM factoids
ORDER by created_at DESC LIMIT %s""", limit)
L = [repr(t[0]) for t in cursor.fetchall()]
return '%s: %s' % \
(utils.nItems(len(L), 'factoid', between='latest'),
(utils.nItems('factoid', len(L), between='latest'),
utils.commaAndify(L))
def _mostPopular(self, cursor, limit):
@ -556,7 +556,7 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
raise self.MostException, 'No factoids have been requested.'
L = ['%r (%s)' % (t[0], t[1]) for t in cursor.fetchall()]
return 'Top %s: %s' % \
(utils.nItems(len(L), 'factoid', between='requested'),
(utils.nItems('factoid', len(L), between='requested'),
utils.commaAndify(L))
def listauth(self, irc, msg, args):

View File

@ -64,7 +64,7 @@ class Movies(callbacks.Privmsg):
'It\'s been rated %s out of 10. ' \
'More information is available at <%s>' % \
(title, movie.year(), genres,
utils.pluralize(len(movie.genres()), 'genre'),
utils.pluralize('genre', len(movie.genres())),
movie.rating(), movie.url)
return s

View File

@ -114,7 +114,7 @@ class Note(callbacks.Privmsg):
unread = int(cursor.fetchone()[0])
s = 'You have %s; ' \
'%s that I haven\'t told you about before now..' % \
(utils.nItems(unread, 'note', 'unread'), unnotified)
(utils.nItems('note', unread, 'unread'), unnotified)
irc.queueMsg(ircmsgs.privmsg(msg.nick, s))
cursor.execute("""UPDATE notes SET notified=1
WHERE notes.to_id=%s""", id)

View File

@ -110,8 +110,8 @@ class Quotes(plugins.ChannelDBHandler, callbacks.Privmsg):
maxid = int(cursor.fetchone()[0])
if maxid is None:
maxid = 0
QUOTE = utils.pluralize(maxid, 'quote')
s = 'There %s %s %s in my database.' % (utils.be(maxid), maxid, QUOTE)
s = 'There %s %s in my database.' % \
(utils.be(maxid), utils.nItems('quote', maxid))
irc.reply(msg, s)
def get(self, irc, msg, args):

View File

@ -176,7 +176,7 @@ class Status(callbacks.Privmsg):
'seconds of CPU time. Out of %s I have %s active.' %
(user, system, user + system,
childUser, childSystem, childUser + childSystem,
utils.nItems(world.threadsSpawned, 'thread', 'spawned'),
utils.nItems('thread', world.threadsSpawned, 'spawned'),
activeThreads))
mem = None
pid = os.getpid()
@ -211,9 +211,9 @@ class Status(callbacks.Privmsg):
attr == callbacks.canonicalName(attr):
commands += 1
s = 'I offer a total of %s in %s. I have processed %s.' % \
(utils.nItems(commands, 'command'),
utils.nItems(callbacksPrivmsg, 'plugin', 'command-based'),
utils.nItems(world.commandsProcessed, 'command'))
(utils.nItems('command', commands),
utils.nItems('plugin', callbacksPrivmsg, 'command-based'),
utils.nItems('command', world.commandsProcessed))
irc.reply(msg, s)
def commands(self, irc, msg, args):

View File

@ -247,20 +247,18 @@ class Todo(callbacks.Privmsg):
_sqlTrans = string.maketrans('*?', '%_')
def search(self, irc, msg, args):
"""[--{regexp,exact}=<value>] [<glob>]
"""[--{regexp}=<value>] [<glob>]
Searches the keyspace for tasks matching <glob>. If --regexp is given,
Searches the todos for tasks matching <glob>. If --regexp is given,
its associated value is taken as a regexp and matched against the
tasks; if --exact is given, its associated value is taken as an exact
string to match against the tasks.
tasks.
"""
try:
id = ircdb.users.getUserId(msg.prefix)
except KeyError:
irc.error(msg, conf.replyNotRegistered)
return
(optlist, rest) = getopt.getopt(args, '', ['regexp=', 'exact='])
(optlist, rest) = getopt.getopt(args, '', ['regexp='])
if not optlist and not rest:
raise callbacks.ArgumentError
db = self.dbHandler.getDb()
@ -268,10 +266,7 @@ class Todo(callbacks.Privmsg):
formats = []
predicateName = 'p'
for (option, arg) in optlist:
if option == '--exact':
criteria.append('task LIKE %s')
formats.append('%' + arg + '%')
elif option == '--regexp':
if option == '--regexp':
criteria.append('%s(task)' % predicateName)
try:
r = utils.perlReToPythonRe(arg)

View File

@ -76,6 +76,13 @@ class Topic(callbacks.Privmsg, configurable.Mixin):
separator = self.configurables.get('separator', channel)
return separator.join(topics)
def _unformatTopic(self, topic, channel):
m = self.topicUnformatter.match(topic)
if m:
return (m.group(1), m.group(2))
else:
return (topic, '')
def add(self, irc, msg, args, channel):
"""[<channel>] <topic>
@ -168,6 +175,22 @@ class Topic(callbacks.Privmsg, configurable.Mixin):
irc.error(msg, 'There are no topics to reorder.')
reorder = privmsgs.checkChannelCapability(reorder, 'topic')
def list(self, irc, msg, args, channel):
"""[<channel>] <number>
Returns a list of the topics in <channel>, prefixed by their indexes.
Mostly useful for topic reordering. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
topics = self._splitTopic(irc.state.getTopic(channel), channel)
L = []
for (i, t) in enumerate(topics):
(t, _) = self._unformatTopic(t, channel)
L.append('%s: %s' % (i+1, utils.ellipsisify(t, 30)))
s = utils.commaAndify(L)
irc.reply(msg, s)
list = privmsgs.channel(list)
def get(self, irc, msg, args, channel):
"""[<channel>] <number>
@ -189,11 +212,7 @@ class Topic(callbacks.Privmsg, configurable.Mixin):
topics = self._splitTopic(irc.state.getTopic(channel), channel)
if topics:
try:
match = self.topicUnformatter.match(topics[number])
if match:
irc.reply(msg, match.group(1))
else:
irc.reply(msg, topics[number])
irc.reply(msg, self._unformatTopic(topics[number], channel)[0])
except IndexError:
irc.error(msg, 'That\'s not a valid topic.')
else:
@ -233,11 +252,7 @@ class Topic(callbacks.Privmsg, configurable.Mixin):
irc.error(msg, 'There are no topics to change.')
return
topic = topics.pop(number)
match = self.topicUnformatter.match(topic)
if match is None:
name = ''
else:
(topic, name) = match.groups()
(topic, name) = self._unformatTopic(topic, channel)
try:
senderName = ircdb.users.getUser(msg.prefix).name
except KeyError:
@ -279,11 +294,7 @@ class Topic(callbacks.Privmsg, configurable.Mixin):
except IndexError:
irc.error(msg, 'That\'s not a valid topic number.')
return
match = self.topicUnformatter.match(topic)
if match is None:
name = ''
else:
(topic, name) = match.groups()
(topic, name) = self._unformatTopic(topic, channel)
try:
username = ircdb.users.getUser(msg.prefix).name
except KeyError:

View File

@ -318,7 +318,7 @@ class Misc(callbacks.Privmsg):
chunk = L.pop()
if L:
chunk += ' \x02(%s)\x0F' % \
utils.nItems(len(L), 'message', 'more')
utils.nItems('message', len(L), 'more')
irc.reply(msg, chunk, True)
except KeyError:
irc.error(msg, 'You haven\'t asked me a command!')

View File

@ -337,7 +337,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
if gc.garbage:
irc.reply(msg, 'Garbage! %r' % gc.garbage)
else:
irc.reply(msg, '%s collected.' % utils.nItems(collected, 'object'))
irc.reply(msg, '%s collected.' % utils.nItems('object', collected))
def set(self, irc, msg, args):
"""<name> <value>

View File

@ -445,8 +445,9 @@ class IrcObjectProxy:
msgs.reverse()
response = msgs.pop()
if msgs:
response = ircutils.bold('(%s)')
response %= utils.nItems(len(msgs), 'message', 'more')
n = ircutils.bold('(%s)')
n %= utils.nItems('message', len(msgs), 'more')
response = '%s %s' % (response, n)
mask = msg.prefix.split('!', 1)[1]
Privmsg._mores[mask] = msgs
private = self.private or not ircutils.isChannel(msg.args[0])

View File

@ -124,31 +124,31 @@ def timeElapsed(elapsed, leadingZeroes=False, years=True, weeks=True,
if leadingZeroes or yrs:
if yrs:
leadingZeroes = True
ret.append(nItems(yrs, 'year'))
ret.append(nItems('year', yrs))
if weeks:
wks, elapsed = elapsed // 604800, elapsed % 604800
if leadingZeroes or wks:
if wks:
leadingZeroes = True
ret.append(nItems(wks, 'week'))
ret.append(nItems('week', wks))
if days:
ds, elapsed = elapsed // 86400, elapsed % 86400
if leadingZeroes or ds:
if ds:
leadingZeroes = True
ret.append(nItems(ds, 'day'))
ret.append(nItems('day', ds))
if hours:
hrs, elapsed = elapsed // 3600, elapsed % 3600
if leadingZeroes or hrs:
if hrs:
leadingZeroes = True
ret.append(nItems(hrs, 'hour'))
ret.append(nItems('hour', hrs))
if minutes or seconds:
mins, secs = elapsed // 60, elapsed % 60
if leadingZeroes or mins:
ret.append(nItems(mins, 'minute'))
ret.append(nItems('minute', mins))
if seconds:
ret.append(nItems(secs, 'second'))
ret.append(nItems('second', secs))
if len(ret) == 0:
raise ValueError, 'Time difference not great enough to be noted.'
if len(ret) == 1:
@ -317,7 +317,7 @@ def _matchCase(s1, s2):
L[i] = char.upper()
return ''.join(L)
def pluralize(i, s):
def pluralize(s, i=2):
"""Returns the plural of s based on its number i. Put any exceptions to
the general English rule of appending 's' in the plurals dictionary.
"""
@ -345,22 +345,22 @@ def depluralize(s):
else:
return s # Don't know what to do.
def nItems(n, item, between=None):
def nItems(item, n, between=None):
"""Works like this:
>>> nItems(1, 'clock')
>>> nItems('clock', 1)
'1 clock'
>>> nItems(10, 'clock')
>>> nItems('clock', 10)
'10 clocks'
>>> nItems(10, 'clock', between='grandfather')
>>> nItems('clock', 10, between='grandfather')
'10 grandfather clocks'
"""
if between is None:
return '%s %s' % (n, pluralize(n, item))
return '%s %s' % (n, pluralize(item, n))
else:
return '%s %s %s' % (n, between, pluralize(n, item))
return '%s %s %s' % (n, between, pluralize(item, n))
def be(i):
"""Returns the form of the verb 'to be' based on the number i."""

View File

@ -113,8 +113,6 @@ if sqlite is not None:
self.assertRegexp('todo search task*',
'#1: task number one and #2: task number two is '
'much longer than task number...')
self.assertRegexp('todo search --exact "task number one"',
'#1: task number one')
self.assertError('todo search --regexp s/bustedregex')
self.assertRegexp('todo search --regexp m/task/',
'#1: task number one and #2: task number two is '

View File

@ -104,5 +104,14 @@ class TopicTestCase(ChannelPluginTestCase, PluginDocumentation):
_ = self.getMsg('topic remove 1')
self.assertError('topic reorder 0')
def testList(self):
_ = self.getMsg('topic add foo')
self.assertResponse('topic list', '1: foo')
_ = self.getMsg('topic add bar')
self.assertResponse('topic list', '1: foo and 2: bar')
_ = self.getMsg('topic add baz')
self.assertResponse('topic list', '1: foo, 2: bar, and 3: baz')
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -37,14 +37,14 @@ import utils
class UtilsTest(unittest.TestCase):
def testPluralize(self):
f = utils.pluralize
self.assertEqual('bike', f(1, 'bike'))
self.assertEqual('bikes', f(2, 'bike'))
self.assertEqual('BIKE', f(1, 'BIKE'))
self.assertEqual('BIKES', f(2, 'BIKE'))
self.assertEqual('match', f(1, 'match'))
self.assertEqual('matches', f(2, 'match'))
self.assertEqual('Patch', f(1, 'Patch'))
self.assertEqual('Patches', f(2, 'Patch'))
self.assertEqual('bike', f('bike', 1))
self.assertEqual('bikes', f('bike', 2))
self.assertEqual('BIKE', f('BIKE', 1))
self.assertEqual('BIKES', f('BIKE', 2))
self.assertEqual('match', f('match', 1))
self.assertEqual('matches', f('match', 2))
self.assertEqual('Patch', f('Patch', 1))
self.assertEqual('Patches', f('Patch', 2))
def testDepluralize(self):
f = utils.depluralize
@ -221,10 +221,10 @@ class UtilsTest(unittest.TestCase):
self.assertEqual(utils.sorted(L, mycmp), ['c', 'b', 'a'])
def testNItems(self):
self.assertEqual(utils.nItems(1, 'tool', 'crazy'), '1 crazy tool')
self.assertEqual(utils.nItems(1, 'tool'), '1 tool')
self.assertEqual(utils.nItems(2, 'tool', 'crazy'), '2 crazy tools')
self.assertEqual(utils.nItems(2, 'tool'), '2 tools')
self.assertEqual(utils.nItems('tool', 1, 'crazy'), '1 crazy tool')
self.assertEqual(utils.nItems('tool', 1), '1 tool')
self.assertEqual(utils.nItems('tool', 2, 'crazy'), '2 crazy tools')
self.assertEqual(utils.nItems('tool', 2), '2 tools')
def testItersplit(self):
from utils import itersplit

View File

@ -106,6 +106,9 @@ class PluginTestCase(unittest.TestCase):
cleanDataDir = True
def setUp(self, nick='test'):
# Set conf variables appropriately.
if self.__class__ in (PluginTestCase, ChannelPluginTestCase):
# Necessary because there's a test in here that shouldn\'t run.
return
conf.prefixChars = '@'
conf.replyWhenNotCommand = False
self.myVerbose = world.myVerbose
@ -137,6 +140,9 @@ class PluginTestCase(unittest.TestCase):
cb = Owner.loadPluginClass(self.irc, module)
def tearDown(self):
if self.__class__ in (PluginTestCase, ChannelPluginTestCase):
# Necessary because there's a test in here that shouldn\'t run.
return
self.irc.die()
gc.collect()
@ -262,9 +268,29 @@ class PluginTestCase(unittest.TestCase):
self.failUnless(re.search(regexp, s, flags),
'%r does not match %r' % (s, regexp))
def testDocumentation(self):
if self.__class__ in (PluginTestCase, ChannelPluginTestCase):
return
for cb in self.irc.callbacks:
name = cb.name()
if (name in ('Admin', 'Channel', 'Misc', 'Owner', 'User') and \
not name.lower() in self.__class__.__name__.lower()) or \
isinstance(cb, callbacks.PrivmsgRegexp):
continue
self.failUnless(sys.modules[cb.__class__.__name__].__doc__,
'%s has no module documentation.' % name)
if hasattr(cb, 'isCommand'):
for attr in dir(cb):
if cb.isCommand(attr):
self.failUnless(getattr(cb, attr, None).__doc__,
'%s.%s has no help.' % (name, attr))
class ChannelPluginTestCase(PluginTestCase):
channel = '#test'
def setUp(self):
if self.__class__ in (PluginTestCase, ChannelPluginTestCase):
return
PluginTestCase.setUp(self)
self.irc.feedMsg(ircmsgs.join(self.channel, prefix=self.prefix))
@ -307,33 +333,7 @@ class ChannelPluginTestCase(PluginTestCase):
class PluginDocumentation:
def testAllCommandsHaveHelp(self):
for cb in self.irc.callbacks:
if isinstance(cb, callbacks.PrivmsgRegexp):
continue
if hasattr(cb, 'isCommand'):
for attr in cb.__class__.__dict__:
if cb.isCommand(attr):
self.failUnless(getattr(cb, attr).__doc__,
'%s has no syntax' % attr)
def testAllCommandsHaveMorehelp(self):
for cb in self.irc.callbacks:
if isinstance(cb, callbacks.PrivmsgRegexp):
continue
if hasattr(cb, 'isCommand'):
for attr in cb.__class__.__dict__:
if cb.isCommand(attr):
command = getattr(cb, attr)
helps = command.__doc__
self.failUnless(helps and len(helps.splitlines()) >= 3,
'%s has no help' % attr)
def testPluginHasDocumentation(self):
for cb in self.irc.callbacks:
m = sys.modules[cb.__class__.__module__]
self.failIf(m.__doc__ is None,
'%s has no module documentation'%cb.__class__.__name__)
pass # This is old stuff, it should be removed some day.