From cbd5abbab7df33052c2532cb99c6c7c735076196 Mon Sep 17 00:00:00 2001 From: James Vega Date: Mon, 25 Oct 2004 22:59:03 +0000 Subject: [PATCH] A bunch more %r -> %s conversions as well as wrap updates, new policy for Channel.voice and some bug fixes for Debian --- plugins/Debian.py | 18 ++--- src/Channel.py | 59 ++++++++------ src/Misc.py | 6 +- src/Owner.py | 20 ++--- src/User.py | 178 ++++++++++++------------------------------ src/callbacks.py | 10 ++- src/commands.py | 7 ++ src/conf.py | 2 +- src/ircdb.py | 13 +-- src/irclib.py | 3 +- src/ircmsgs.py | 8 +- src/ircutils.py | 14 ++-- src/log.py | 5 +- src/utils.py | 14 ++-- test/test_Channel.py | 2 +- test/test_Debian.py | 18 ++--- test/test_commands.py | 7 ++ 17 files changed, 172 insertions(+), 212 deletions(-) diff --git a/plugins/Debian.py b/plugins/Debian.py index ae2ace843..5dd83b289 100644 --- a/plugins/Debian.py +++ b/plugins/Debian.py @@ -124,7 +124,7 @@ class Debian(callbacks.Privmsg, # that). if not optlist and not glob: raise callbacks.ArgumentError - if optlist and glob > 1: + if optlist and glob: irc.error('You must specify either a glob or a regexp/exact ' 'search, but not both.') for (option, arg) in optlist: @@ -181,8 +181,8 @@ class Debian(callbacks.Privmsg, irc.reply('I found no packages with that file.') else: irc.reply(utils.commaAndify(packages)) - file = wrap(file, [getopts({'regexp':'regexpMatcher', 'exact':'text'}), - additional('text')]) + file = wrap(file, [getopts({'regexp':'regexpMatcher','exact':'something'}), + additional('glob')]) _debreflags = re.DOTALL | re.IGNORECASE _deblistre = re.compile(r'

Package ([^<]+)

(.*?)', _debreflags) @@ -264,8 +264,8 @@ class Debian(callbacks.Privmsg, archPredicate = lambda s, arg=arg: (arg in s) predicates.append(archPredicate) for glob in globs: - glob = glob.replace('*', '.*').replace('?', '.?') - predicates.append(re.compile(r'.*%s.*' % glob).search) + glob = fnmatch.translate(glob) + predicates.append(re.compile(glob).search) packages = [] try: fd = webutils.getUrlFd('http://incoming.debian.org/') @@ -285,18 +285,16 @@ class Debian(callbacks.Privmsg, irc.reply(utils.commaAndify(packages)) incoming = thread(wrap(incoming, [getopts({'regexp':'regexpMatcher', 'arch':'text'}), - any('text')])) + any('glob')])) _newpkgre = re.compile(r'
  • ]+>([^<]+)') - def new(self, irc, msg, args, optlist, glob): + def new(self, irc, msg, args, section, glob): """[{main,contrib,non-free}] [] Checks for packages that have been added to Debian's unstable branch in the past week. If no glob is specified, returns a list of all packages. If no section is specified, defaults to main. """ - if '?' not in glob and '*' not in glob: - glob = '*%s*' % glob try: fd = webutils.getUrlFd( 'http://packages.debian.org/unstable/newpkg_%s' % section) @@ -316,7 +314,7 @@ class Debian(callbacks.Privmsg, irc.error('No packages matched that search.') new = wrap(new, [optional(('literal', ('main', 'contrib', 'non-free')), 'main'), - additional('text', '*')]) + additional('glob', '*')]) _severity = re.compile(r'.*(?:severity set to `([^\']+)\'|' r'severity:\s+([^\s]+))', re.I) diff --git a/src/Channel.py b/src/Channel.py index 4442bc970..023b7f8bc 100755 --- a/src/Channel.py +++ b/src/Channel.py @@ -163,7 +163,7 @@ class Channel(callbacks.Privmsg): irc.queueMsg(ircmsgs.halfops(channel, nicks)) halfop = wrap(halfop, [('checkChannelCapability', 'halfop'), ('haveOp', 'halfop someone'), - many('nickInChannel')]) + any('nickInChannel')]) def voice(self, irc, msg, args, channel, nicks): """[] [ ...] @@ -173,12 +173,21 @@ class Channel(callbacks.Privmsg): voice you. is only necessary if the message isn't sent in the channel itself. """ - if not nicks: + if nicks: + if len(nicks) == 1 and msg.nick in nicks: + capability = 'voice' + else: + capability = 'op' + else: nicks = [msg.nick] - irc.queueMsg(ircmsgs.voices(channel, nicks)) - voice = wrap(voice, [('checkChannelCapability', 'voice'), - ('haveOp', 'voice someone'), - many('nickInChannel')]) + capability = 'voice' + capability = ircdb.makeChannelCapability(channel, capability) + if ircdb.checkCapability(msg.prefix, capability): + irc.queueMsg(ircmsgs.voices(channel, nicks)) + else: + irc.errorNoCapability(capability) + voice = wrap(voice, ['channel', ('haveOp', 'voice someone'), + any('nickInChannel')]) def deop(self, irc, msg, args, channel, nicks): """[] [ ...] @@ -193,11 +202,10 @@ class Channel(callbacks.Privmsg): 'yourself.', Raise=True) if not nicks: nicks = [msg.nick] - else: - irc.queueMsg(ircmsgs.deops(channel, nicks)) + irc.queueMsg(ircmsgs.deops(channel, nicks)) deop = wrap(deop, [('checkChannelCapability', 'op'), ('haveOp', 'deop someone'), - many('nickInChannel')]) + any('nickInChannel')]) def dehalfop(self, irc, msg, args, channel, nicks): """[] [ ...] @@ -212,11 +220,10 @@ class Channel(callbacks.Privmsg): 'dehalfop me yourself.', Raise=True) if not nicks: nicks = [msg.nick] - else: - irc.queueMsg(ircmsgs.dehalfops(channel, nicks)) + irc.queueMsg(ircmsgs.dehalfops(channel, nicks)) dehalfop = wrap(dehalfop, [('checkChannelCapability', 'halfop'), ('haveOp', 'dehalfop someone'), - many('nickInChannel')]) + any('nickInChannel')]) # XXX These nicks should really be sets, rather than lists, especially # we check whether the bot's nick is in them. @@ -235,11 +242,10 @@ class Channel(callbacks.Privmsg): 'me yourself.', Raise=True) if not nicks: nicks = [msg.nick] - else: - irc.queueMsg(ircmsgs.devoices(channel, nicks)) + irc.queueMsg(ircmsgs.devoices(channel, nicks)) devoice = wrap(devoice, [('checkChannelCapability', 'voice'), ('haveOp', 'devoice someone'), - many('nickInChannel')]) + any('nickInChannel')]) def cycle(self, irc, msg, args, channel, key): """[] [] @@ -299,11 +305,13 @@ class Channel(callbacks.Privmsg): # Check that they're not trying to make us kickban ourself. self.log.debug('In kban') if not ircutils.isNick(bannedNick): - self.log.warning('%r tried to kban a non nick: %r', - msg.prefix, bannedNick) + self.log.warning('%s tried to kban a non nick: %s', + utils.quoted(msg.prefix), + utils.quoted(bannedNick)) raise callbacks.ArgumentError elif bannedNick == irc.nick: - self.log.warning('%r tried to make me kban myself.', msg.prefix) + self.log.warning('%s tried to make me kban myself.', + utils.quoted(msg.prefix)) irc.error('I cowardly refuse to kickban myself.') return if not reason: @@ -337,7 +345,8 @@ class Channel(callbacks.Privmsg): # Check (again) that they're not trying to make us kickban ourself. if ircutils.hostmaskPatternEqual(banmask, irc.prefix): if ircutils.hostmaskPatternEqual(banmask, irc.prefix): - self.log.warning('%r tried to make me kban myself.',msg.prefix) + self.log.warning('%s tried to make me kban myself.', + utils.quoted(msg.prefix)) irc.error('I cowardly refuse to ban myself.') return else: @@ -360,15 +369,16 @@ class Channel(callbacks.Privmsg): doBan() elif ircdb.checkCapability(msg.prefix, capability): if ircdb.checkCapability(bannedHostmask, capability): - self.log.warning('%s tried to ban %r, but both have %s', - msg.prefix, bannedHostmask, capability) + self.log.warning('%s tried to ban %s, but both have %s', + msg.prefix, utils.quoted(bannedHostmask), + capability) irc.error('%s has %s too, you can\'t ban him/her/it.' % (bannedNick, capability)) else: doBan() else: - self.log.warning('%r attempted kban without %s', - msg.prefix, capability) + self.log.warning('%s attempted kban without %s', + utils.quoted(msg.prefix), capability) irc.errorNoCapability(capability) exact,nick,user,host kban = wrap(kban, @@ -519,7 +529,8 @@ class Channel(callbacks.Privmsg): # XXX Add the expirations. c = ircdb.channels.getChannel(channel) if len(c.ignores) == 0: - s = 'I\'m not currently ignoring any hostmasks in %r' % channel + s = 'I\'m not currently ignoring any hostmasks in %s' % \ + utils.quoted(channel) irc.reply(s) else: L = sorted(c.ignores) diff --git a/src/Misc.py b/src/Misc.py index 56ce1bf44..bc953e08a 100755 --- a/src/Misc.py +++ b/src/Misc.py @@ -387,8 +387,8 @@ class Misc(callbacks.Privmsg): if irc.nested: irc.reply(utils.commaAndify(names)) else: - irc.reply('The %r command is available in the %s %s.' % - (command, plugin, + irc.reply('The %s command is available in the %s %s.' % + (utils.quoted(command), plugin, utils.pluralize('plugin', len(names)))) else: irc.error('There is no such command %s.' % command) @@ -675,7 +675,7 @@ class Misc(callbacks.Privmsg): if getattr(module, '__author__', False) == authorInfo: isAuthor = True # XXX Partition needs moved to utils. - (nonCommands, commands) = fix.partition(lambda s: ' ' in s, + (nonCommands, commands) = fix.partition(lambda s: ' ' in s, contributions) results = [] if commands: diff --git a/src/Owner.py b/src/Owner.py index 80e1fbcd4..fe2a3204b 100644 --- a/src/Owner.py +++ b/src/Owner.py @@ -75,7 +75,8 @@ def loadPluginModule(name, ignoreDeprecation=False): try: files.extend(os.listdir(dir)) except EnvironmentError: # OSError, IOError superclass. - log.warning('Invalid plugin directory: %r; removing.', dir) + log.warning('Invalid plugin directory: %s; removing.', + utils.quoted(dir)) conf.supybot.directories.plugins().remove(dir) loweredFiles = map(str.lower, files) try: @@ -98,7 +99,8 @@ def loadPluginModule(name, ignoreDeprecation=False): if ignoreDeprecation: log.warning('Deprecated plugin loaded: %s', name) else: - raise Deprecated, 'Attempted to load deprecated plugin %r' % name + raise Deprecated, 'Attempted to load deprecated plugin %s' % \ + utils.quoted(name) if module.__name__ in sys.modules: sys.modules[module.__name__] = module linecache.checkcache() @@ -173,8 +175,8 @@ def renameCommand(cb, name, newName): method = getattr(cb.__class__, name) setattr(cb.__class__, newName, method) delattr(cb.__class__, name) - - + + registerDefaultPlugin('list', 'Misc') registerDefaultPlugin('help', 'Misc') registerDefaultPlugin('ignore', 'Admin') @@ -248,7 +250,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg): # There are no plugins that are all-lowercase, so we'll at # least attempt to capitalize them. if name == name.lower(): - name = name.capitalize() + name = name.capitalize() conf.registerPlugin(name) if name.startswith('supybot.commands.defaultPlugins'): try: @@ -266,7 +268,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg): self.log.warning('Tried to send a message to myself: %r.', msg) return None return msg - + def isCommand(self, name): return name == 'log' or \ self.__parent.isCommand(name) @@ -396,7 +398,8 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg): self._evalEnv['_'] = x irc.reply(repr(x)) except SyntaxError, e: - irc.reply('%s: %r' % (utils.exnToString(e), s)) + irc.reply('%s: %s' % (utils.exnToString(e), + utils.quoted(s))) except Exception, e: self.log.exception('Uncaught exception in Owner.eval. ' 'This is not a bug. Please do not ' @@ -491,7 +494,6 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg): Sends the raw string given to the server. """ - s = privmsgs.getArgs(args) try: m = ircmsgs.IrcMsg(s) except Exception, e: @@ -678,7 +680,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg): irc.replySuccess() defaultcapability = wrap(defaultcapability, [('literal', ['add','remove']), 'capability']) - + def disable(self, irc, msg, args): """[] diff --git a/src/User.py b/src/User.py index 7aee66aab..30c2dbfcb 100755 --- a/src/User.py +++ b/src/User.py @@ -46,6 +46,7 @@ from itertools import imap, ifilter import supybot.conf as conf import supybot.utils as utils import supybot.ircdb as ircdb +from supybot.commands import * import supybot.ircutils as ircutils import supybot.privmsgs as privmsgs import supybot.callbacks as callbacks @@ -55,26 +56,22 @@ class User(callbacks.Privmsg): if password and ircutils.isChannel(msg.args[0]): raise callbacks.Error, conf.supybot.replies.requiresPrivacy() - def list(self, irc, msg, args): - """[--capability ] [] + def list(self, irc, msg, args, optlist, glob): + """[--capability=] [] Returns the valid registered usernames matching . If is not given, returns all registered usernames. """ - (optlist, rest) = getopt.getopt(args, '', ['capability=']) predicates = [] for (option, arg) in optlist: - if option == '--capability': + if option == 'capability': def p(u, cap=arg): try: return u.checkCapability(cap) except KeyError: return False predicates.append(p) - glob = privmsgs.getArgs(rest, required=0, optional=1) if glob: - if '*' not in glob and '?' not in glob: - glob = '*%s*' % glob r = re.compile(fnmatch.translate(glob), re.I) def p(u): return r.match(u.name) is not None @@ -94,8 +91,10 @@ class User(callbacks.Privmsg): irc.reply('There are no matching registered users.') else: irc.reply('There are no registered users.') + list = wrap(list, [getopts({'capability':'capability'}), + additional('glob')]) - def register(self, irc, msg, args): + def register(self, irc, msg, args, optlist, name, password): """[--hashed] Registers with the given password and the current @@ -104,14 +103,11 @@ class User(callbacks.Privmsg): not in a channel. If --hashed is given, the password will be hashed on disk, rather than being stored in the default configured format. """ - (optlist, rest) = getopt.getopt(args, '', ['hashed']) - (name, password) = privmsgs.getArgs(rest, required=2) addHostmask = True hashed = conf.supybot.databases.users.hash() for (option, arg) in optlist: - if option == '--hashed': + if option == 'hashed': hashed = True - self._checkNotChannel(irc, msg, password) try: ircdb.users.getUserId(name) irc.error('That name is already assigned to someone.', Raise=True) @@ -136,30 +132,26 @@ class User(callbacks.Privmsg): user.addHostmask(msg.prefix) ircdb.users.setUser(user) irc.replySuccess() + register = wrap(register, ['private', getopts({'hashed':''}), 'something', + 'something']) - def unregister(self, irc, msg, args): + def unregister(self, irc, msg, args, user, password): """ [] Unregisters from the user database. If the user giving this command is an owner user, the password is not necessary. """ - (name, password) = privmsgs.getArgs(args, optional=1) - self._checkNotChannel(irc, msg, password) - try: - id = ircdb.users.getUserId(name) - u = ircdb.users.getUser(id) - except KeyError: - irc.error('That username isn\'t registered.') - return - if not u.checkPassword(password): - u = ircdb.users.getUser(msg.prefix) - if not u.checkCapability('owner'): + if not user.checkPassword(password): + user = ircdb.users.getUser(msg.prefix) + if not user.checkCapability('owner'): irc.error(conf.supybot.replies.incorrectAuthentication()) return - ircdb.users.delUser(id) + ircdb.users.delUser(user.id) irc.replySuccess() + unregister = wrap(unregister, ['private', 'otherUser', + additional('something')]) - def changename(self, irc, msg, args): + def changename(self, irc, msg, args, user, newname, password): """ [] Changes your current user database name to the new name given. @@ -167,17 +159,9 @@ class User(callbacks.Privmsg): If you include the parameter, this message must be sent to the bot privately (not on a channel). """ - (name,newname,password) = privmsgs.getArgs(args,required=2,optional=1) - self._checkNotChannel(irc, msg, password) - try: - id = ircdb.users.getUserId(name) - user = ircdb.users.getUser(id) - except KeyError: - irc.error('That username isn\'t registered.') - return try: id = ircdb.users.getUserId(newname) - irc.error('%r is already registered.' % newname) + irc.error('%s is already registered.' % utils.quoted(newname)) return except KeyError: pass @@ -185,8 +169,10 @@ class User(callbacks.Privmsg): user.name = newname ircdb.users.setUser(user) irc.replySuccess() + changename = wrap(changename, ['private', 'otherUser', 'something', + additional('something')]) - def addhostmask(self, irc, msg, args): + def addhostmask(self, irc, msg, args, user, hostmask, password): """[] [] [] Adds the hostmask to the user specified by . The @@ -198,12 +184,8 @@ class User(callbacks.Privmsg): hostmask. If is not given, it defaults to your currently identified name. """ - (name, hostmask, password) = privmsgs.getArgs(args, 0, 3) - self._checkNotChannel(irc, msg, password) if not hostmask: hostmask = msg.prefix - if not name: - name = msg.prefix if not ircutils.isUserHostmask(hostmask): irc.errorInvalid('hostmask', hostmask, 'Make sure your hostmask ' 'includes a nick, then an exclamation point (!), then ' @@ -211,11 +193,6 @@ class User(callbacks.Privmsg): 'free to use wildcards (* and ?, which work just like ' 'they do on the command line) in any of these parts.', Raise=True) - try: - id = ircdb.users.getUserId(name) - user = ircdb.users.getUser(id) - except KeyError: - irc.errorNoUser(Raise=True) try: otherId = ircdb.users.getUserId(hostmask) if otherId != id: @@ -241,8 +218,11 @@ class User(callbacks.Privmsg): except ValueError, e: irc.error(str(e), Raise=True) irc.replySuccess() + addhostmask = wrap(addhostmask, [first('otherUser', 'user'), + optional('something'), + additional('something')]) - def removehostmask(self, irc, msg, args): + def removehostmask(self, irc, msg, args, user, hostmask, password): """ [] Removes the hostmask from the record of the user specified @@ -251,14 +231,6 @@ class User(callbacks.Privmsg): recognized by his hostmask. If you include the parameter, this message must be sent to the bot privately (not on a channel). """ - (name, hostmask, password) = privmsgs.getArgs(args, 2, 1) - self._checkNotChannel(irc, msg, password) - try: - id = ircdb.users.getUserId(name) - user = ircdb.users.getUser(id) - except KeyError: - irc.errorNoUser() - return if not user.checkPassword(password) and \ not user.checkHostmask(msg.prefix): u = ircdb.users.getUser(msg.prefix) @@ -277,8 +249,10 @@ class User(callbacks.Privmsg): return ircdb.users.setUser(user) irc.replySuccess(s) + removehostmask = wrap(removehostmask, ['private', 'otherUser', 'something', + additional('something')]) - def setpassword(self, irc, msg, args): + def setpassword(self, irc, msg, args, optlist, user, password,newpassword): """[--hashed] Sets the new password for the user specified by to @@ -289,57 +263,36 @@ class User(callbacks.Privmsg): changed isn't that same owner user), then needn't be correct. """ - (optlist, rest) = getopt.getopt(args, '', ['hashed']) - (name, oldpassword, newpassword) = privmsgs.getArgs(rest, 3) hashed = conf.supybot.databases.users.hash() for (option, arg) in optlist: - if option == '--hashed': + if option == 'hashed': hashed = True - self._checkNotChannel(irc, msg, oldpassword+newpassword) - try: - id = ircdb.users.getUserId(name) - user = ircdb.users.getUser(id) - except KeyError: - irc.errorNoUser() - return u = ircdb.users.getUser(msg.prefix) - if user.checkPassword(oldpassword) or \ + if user.checkPassword(password) or \ (u.checkCapability('owner') and not u == user): user.setPassword(newpassword, hashed=hashed) ircdb.users.setUser(user) irc.replySuccess() else: irc.error(conf.supybot.replies.incorrectAuthentication()) + setpassword = wrap(setpassword, [getopts({'hashed':''}), 'otherUser', + 'something', 'something']) - def username(self, irc, msg, args): + def username(self, irc, msg, args, user): """ Returns the username of the user specified by or if the user is registered. """ - hostmask = privmsgs.getArgs(args) - if not ircutils.isUserHostmask(hostmask): - try: - hostmask = irc.state.nickToHostmask(hostmask) - except KeyError: - irc.reply('I couldn\'t find %s in my user database.' %hostmask) - return - try: - user = ircdb.users.getUser(hostmask) - irc.reply(user.name) - except KeyError: - irc.reply('I couldn\'t find %s in my user database.' % hostmask) + irc.reply(user.name) + username = wrap(username, ['otherUser']) - def hostmasks(self, irc, msg, args): + def hostmasks(self, irc, msg, args, name): """[] Returns the hostmasks of the user specified by ; if isn't specified, returns the hostmasks of the user calling the command. """ - if ircutils.isChannel(msg.args[0]): - irc.errorRequiresPrivacy() - return - name = privmsgs.getArgs(args, required=0, optional=1) try: user = ircdb.users.getUser(msg.prefix) if name: @@ -355,38 +308,24 @@ class User(callbacks.Privmsg): irc.reply(repr(user.hostmasks)) except KeyError: irc.errorNotRegistered() + hostmasks = wrap(hostmasks, ['private', additional('something')]) - def capabilities(self, irc, msg, args): + def capabilities(self, irc, msg, args, user): """[] Returns the capabilities of the user specified by ; if isn't specified, returns the hostmasks of the user calling the command. """ - if not args: - name = msg.prefix - else: - name = privmsgs.getArgs(args) - try: - user = ircdb.users.getUser(name) - irc.reply('[%s]' % '; '.join(user.capabilities)) - except KeyError: - irc.errorNoUser() + irc.reply('[%s]' % '; '.join(user.capabilities)) + capabilities = wrap(capabilities, [first('otherUser', 'user')]) - def identify(self, irc, msg, args): + def identify(self, irc, msg, args, user, password): """ Identifies the user as . This command (and all other commands that include a password) must be sent to the bot privately, not in a channel. """ - (name, password) = privmsgs.getArgs(args, 2) - self._checkNotChannel(irc, msg) - try: - id = ircdb.users.getUserId(name) - user = ircdb.users.getUser(id) - except KeyError: - irc.errorNoUser() - return if user.checkPassword(password): try: user.addAuth(msg.prefix) @@ -397,8 +336,9 @@ class User(callbacks.Privmsg): 'doesn\'t match any of your known hostmasks.') else: irc.error(conf.supybot.replies.incorrectAuthentication()) + identify = wrap(identify, ['private', 'otherUser', 'something']) - def unidentify(self, irc, msg, args): + def unidentify(self, irc, msg, args, user): """takes no arguments Un-identifies you. Note that this may not result in the desired @@ -406,12 +346,6 @@ class User(callbacks.Privmsg): have added hostmasks to your user that can cause the bot to continue to recognize you. """ - try: - id = ircdb.users.getUserId(msg.prefix) - user = ircdb.users.getUser(id) - except KeyError: - irc.errorNoUser() - return user.clearAuth() ircdb.users.setUser(user) irc.replySuccess('If you remain recognized after giving this command, ' @@ -419,6 +353,7 @@ class User(callbacks.Privmsg): 'by password. You must remove whatever hostmask is ' 'causing you to be recognized in order not to be ' 'recognized.') + unidentify = wrap(unidentify, ['user']) def whoami(self, irc, msg, args): """takes no arguments @@ -430,8 +365,9 @@ class User(callbacks.Privmsg): irc.reply(user.name) except KeyError: irc.reply('I don\'t recognize you.') + whoami = wrap(whoami) - def setsecure(self, irc, msg, args): + def setsecure(self, irc, msg, args, user, password, value): """ [] Sets the secure flag on the user of the person sending the message. @@ -441,21 +377,8 @@ class User(callbacks.Privmsg): If a specific True/False value is not given, it inverts the current value. """ - (password, value) = privmsgs.getArgs(args, optional=1) - self._checkNotChannel(irc, msg, password) - try: - id = ircdb.users.getUserId(msg.prefix) - user = ircdb.users.getUser(id) - except KeyError: - irc.errorNotRegistered() - if value == '': + if value is None: value = not user.secure - elif value.lower() in ('true', 'on', 'enable'): - value = True - elif value.lower() in ('false', 'off', 'disable'): - value = False - else: - irc.errorInvalid('boolean value', value, Raise=True) if user.checkPassword(password) and \ user.checkHostmask(msg.prefix, useAuth=False): user.secure = value @@ -463,6 +386,8 @@ class User(callbacks.Privmsg): irc.reply('Secure flag set to %s' % value) else: irc.error(conf.supybot.replies.incorrectAuthentication()) + setsecure = wrap(setsecure, ['private', 'user', 'something', + additional('boolean')]) def stats(self, irc, msg, args): """takes no arguments @@ -488,7 +413,8 @@ class User(callbacks.Privmsg): '%s and %s.' % (users, hostmasks, utils.nItems('owner', owners), utils.nItems('admin', admins))) - + stats = wrap(stats) + ## def config(self, irc, msg, args): ## """[--list] [] diff --git a/src/callbacks.py b/src/callbacks.py index 44c204385..4174f713d 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -480,8 +480,8 @@ class RichReplyMethods(object): if 'Raise' not in kwargs: kwargs['Raise'] = True if isinstance(capability, basestring): # checkCommandCapability! - log.warning('Denying %s for lacking %r capability.', - self.msg.prefix, capability) + log.warning('Denying %s for lacking %s capability.', + self.msg.prefix, utils.quoted(capability)) if not self._getConfig(conf.supybot.reply.noCapabilityError): v = self._getConfig(conf.supybot.replies.noCapability) s = self.__makeReply(v % capability, s) @@ -1092,7 +1092,8 @@ class Privmsg(irclib.IrcCallback): def getCommand(self, name): """Gets the given command from this plugin.""" name = canonicalName(name) - assert self.isCommand(name), '%r is not a command.' % name + assert self.isCommand(name), '%s is not a command.' % \ + utils.quoted(name) return getattr(self, name) def callCommand(self, name, irc, msg, *L, **kwargs): @@ -1243,7 +1244,8 @@ class PrivmsgRegexp(Privmsg): r = re.compile(value.__doc__, self.flags) self.res.append((r, name)) except re.error, e: - self.log.warning('Invalid regexp: %r (%s)',value.__doc__,e) + self.log.warning('Invalid regexp: %s (%s)', + utils.quoted(value.__doc__), e) utils.sortBy(operator.itemgetter(1), self.res) def callCommand(self, name, irc, msg, *L, **kwargs): diff --git a/src/commands.py b/src/commands.py index 9fb7c5a6f..1439ec344 100644 --- a/src/commands.py +++ b/src/commands.py @@ -426,6 +426,12 @@ def checkCapability(irc, msg, args, state, cap): def anything(irc, msg, args, state): state.args.append(args.pop(0)) +def getGlob(irc, msg, args, state): + glob = args.pop(0) + if '*' not in glob and '?' not in glob: + glob = '*%s*' % glob + state.args.append(glob) + def getUrl(irc, msg, args, state): if webutils.urlRe.match(args[0]): state.args.append(args.pop(0)) @@ -521,6 +527,7 @@ wrappers = ircutils.IrcDict({ 'something': getSomething, 'filename': getSomething, # XXX Check for validity. 'commandName': getCommandName, + 'glob': getGlob, 'text': anything, 'somethingWithoutSpaces': getSomethingNoSpaces, 'capability': getSomethingNoSpaces, diff --git a/src/conf.py b/src/conf.py index a5f1cf5d6..e916bdd41 100644 --- a/src/conf.py +++ b/src/conf.py @@ -457,7 +457,7 @@ registerChannelValue(supybot.replies, 'notRegistered', but they're not currently recognized.""")) registerChannelValue(supybot.replies, 'noCapability', - registry.NormalizedString("""You don't have the %r capability. If you + registry.NormalizedString("""You don't have the %s capability. If you think that you should have this capability, be sure that you are identified before trying again. The 'whoami' command can tell you if you're identified.""", """Determines what error message is given when the bot is diff --git a/src/ircdb.py b/src/ircdb.py index 27df766fc..f5e08b101 100644 --- a/src/ircdb.py +++ b/src/ircdb.py @@ -225,10 +225,11 @@ class IrcUser(object): self.hostmasks = hostmasks def __repr__(self): - return '%s(id=%s, ignore=%s, password="", name=%r, hashed=%r, ' \ + return '%s(id=%s, ignore=%s, password="", name=%s, hashed=%r, ' \ 'capabilities=%r, hostmasks=[], secure=%r)\n' % \ - (self.__class__.__name__, self.id, self.ignore, self.name, - self.hashed, self.capabilities, self.secure) + (self.__class__.__name__, self.id, self.ignore, + utils.quoted(self.name), self.hashed, self.capabilities, + self.secure) def __hash__(self): return hash(self.id) @@ -655,7 +656,8 @@ class UsersDictionary(utils.IterableMap): log.error('Multiple matches found in user database. ' 'Removing the offending hostmasks.') for (id, hostmask) in ids.iteritems(): - log.error('Removing %r from user %s.', hostmask, id) + log.error('Removing %s from user %s.', + utils.quoted(hostmask), id) self.users[id].removeHostmask(hostmask) raise DuplicateHostmask, 'Ids %r matched.' % ids else: # Not a hostmask, must be a name. @@ -855,7 +857,8 @@ class IgnoresDB(object): expiration = 0 self.add(hostmask, expiration) except Exception, e: - log.error('Invalid line in ignores database: %r', line) + log.error('Invalid line in ignores database: %s', + utils.quoted(line)) fd.close() def flush(self): diff --git a/src/irclib.py b/src/irclib.py index c334a6e38..c768014af 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -172,7 +172,8 @@ class IrcMsgQueue(object): if msg in self.msgs and \ not conf.supybot.protocols.irc.queueDuplicateMessages(): s = str(msg).strip() - log.warning('Not adding message %r to queue, already added.', s) + log.warning('Not adding message %s to queue, already added.', + utils.quoted(s)) return False else: self.msgs.add(msg) diff --git a/src/ircmsgs.py b/src/ircmsgs.py index 64670f1ae..1db153cdc 100644 --- a/src/ircmsgs.py +++ b/src/ircmsgs.py @@ -43,6 +43,7 @@ import time import string import supybot.conf as conf +import supybot.utils as utils import supybot.ircutils as ircutils ### @@ -197,13 +198,14 @@ class IrcMsg(object): def __repr__(self): if self._repr is not None: return self._repr - self._repr = 'IrcMsg(prefix=%r, command=%r, args=%r)' % \ - (self.prefix, self.command, self.args) + self._repr = 'IrcMsg(prefix=%s, command=%s, args=%r)' % \ + (utils.quoted(self.prefix), utils.quoted(self.command), + self.args) return self._repr def __reduce__(self): return (self.__class__, (str(self),)) - + def tag(self, tag, value=True): self.tags[tag] = value diff --git a/src/ircutils.py b/src/ircutils.py index acfaf6bb2..a2b2b4222 100644 --- a/src/ircutils.py +++ b/src/ircutils.py @@ -100,7 +100,7 @@ def toLower(s, casemapping=None): elif casemapping == 'ascii': # freenode return s.lower() else: - raise ValueError, 'Invalid casemapping: %r' % casemapping + raise ValueError, 'Invalid casemapping: %s' % utils.quoted(casemapping) def strEqual(nick1, nick2): """Returns True if nick1 == nick2 according to IRC case rules.""" @@ -360,7 +360,7 @@ class FormatContext(object): # Should we individually end formatters? s += '\x0f' return s - + class FormatParser(object): def __init__(self, s): self.fd = sio(s) @@ -427,7 +427,7 @@ def wrap(s, length): context = FormatParser(chunk).parse() processed.append(context.end(chunk)) return processed - + def isValidArgument(s): """Returns whether s is strictly a valid argument for an IRC message.""" return '\r' not in s and '\n' not in s and '\x00' not in s @@ -437,7 +437,7 @@ def safeArgument(s): if isinstance(s, unicode): s = s.encode('utf-8') elif not isinstance(s, basestring): - debug('Got a non-string in safeArgument: %r', s) + debug('Got a non-string in safeArgument: %s', utils.quoted(s)) s = str(s) if isValidArgument(s): return s @@ -504,7 +504,7 @@ class IrcSet(utils.NormalizingSet): """A sets.Set using IrcStrings instead of regular strings.""" def normalize(self, s): return IrcString(s) - + def __reduce__(self): return (self.__class__, (list(self),)) @@ -548,7 +548,7 @@ class FloodQueue(object): return q else: return None - + def enqueue(self, msg, what=None): if what is None: what = msg @@ -571,7 +571,7 @@ class FloodQueue(object): if elt == what: return True return False - + mircColors = IrcDict({ 'white': '0', diff --git a/src/log.py b/src/log.py index 8a314c19b..88e791a42 100644 --- a/src/log.py +++ b/src/log.py @@ -88,7 +88,8 @@ class Logger(logging.Logger): eId = hex(hash(eStrId) & 0xFFFFF) logging.Logger.exception(self, *args) if hasattr(e, '__revision__') and e.__revision__: - self.error('Exception __revision__: %r', e.__revision__) + self.error('Exception __revision__: %s', + utils.quoted(e.__revision__)) self.error('Exception id: %s', eId) # The traceback should be sufficient if we want it. # self.error('Exception string: %s', eStrId) @@ -290,7 +291,7 @@ registry.exception = exception def stat(*args): level = conf.supybot.log.statistics() _logger.log(level, *args) - + setLevel = _logger.setLevel atexit.register(logging.shutdown) diff --git a/src/utils.py b/src/utils.py index 26c475b2c..98ad51631 100755 --- a/src/utils.py +++ b/src/utils.py @@ -507,10 +507,10 @@ def safeEval(s, namespace={'True': True, 'False': False, 'None': None}): if node.__class__ is compiler.ast.Module: return node.doc else: - raise ValueError, 'Unsafe string: %r' % s + raise ValueError, 'Unsafe string: %s' % quoted(s) node = nodes[0] if node.__class__ is not compiler.ast.Discard: - raise ValueError, 'Invalid expression: %r' % s + raise ValueError, 'Invalid expression: %s' % quoted(s) node = node.getChildNodes()[0] def checkNode(node): if node.__class__ is compiler.ast.Const: @@ -529,7 +529,7 @@ def safeEval(s, namespace={'True': True, 'False': False, 'None': None}): if checkNode(node): return eval(s, namespace, namespace) else: - raise ValueError, 'Unsafe string: %r' % s + raise ValueError, 'Unsafe string: %s' % quoted(s) def exnToString(e): """Turns a simple exception instance into a string (better than str(e))""" @@ -726,7 +726,7 @@ class AtomicFile(file): def __init__(self, filename, mode='w', allowEmptyOverwrite=True, makeBackupIfSmaller=True, tmpDir=None, backupDir=None): if mode not in ('w', 'wb'): - raise ValueError, 'Invalid mode: %r' % mode + raise ValueError, 'Invalid mode: %s' % quoted(mode) self.rolledback = False self.allowEmptyOverwrite = allowEmptyOverwrite self.makeBackupIfSmaller = makeBackupIfSmaller @@ -826,12 +826,12 @@ def toBool(s): elif s in ('false', 'off', 'disable', 'disabled'): return False else: - raise ValueError, 'Invalid string for toBool: %r' % s - + raise ValueError, 'Invalid string for toBool: %s' % quoted(s) + def mapinto(f, L): for (i, x) in enumerate(L): L[i] = f(x) - + if __name__ == '__main__': import doctest doctest.testmod(sys.modules['__main__']) diff --git a/test/test_Channel.py b/test/test_Channel.py index 71a7274d9..a881c8b81 100644 --- a/test/test_Channel.py +++ b/test/test_Channel.py @@ -33,7 +33,7 @@ import supybot.conf as conf import supybot.ircdb as ircdb import supybot.ircmsgs as ircmsgs -class ChannelTestCase(ChannelPluginTestCase, PluginDocumentation): +class ChannelTestCase(ChannelPluginTestCase): plugins = ('Channel', 'User') def testLobotomies(self): self.assertRegexp('lobotomies', 'not.*any') diff --git a/test/test_Debian.py b/test/test_Debian.py index 9eabbb1d6..af88b9673 100644 --- a/test/test_Debian.py +++ b/test/test_Debian.py @@ -32,29 +32,29 @@ import time from testsupport import * -class DebianTestCase(PluginTestCase, PluginDocumentation): +class DebianTestCase(PluginTestCase): plugins = ('Debian',) timeout = 100 cleanDataDir = False fileDownloaded = False if network: - def setup(self, nick='test'): - plugintestcase.setup(self) + def setUp(self, nick='test'): + PluginTestCase.setUp(self) try: datadir = conf.supybot.directories.data() if os.path.exists(os.path.join(datadir, - 'contents-i386.gz')): + 'Contents-i386.gz')): pass else: print - print "downloading files, this may take awhile" - filename = os.path.join(datadir, 'contents-i386.gz') + print "Downloading files, this may take awhile" + filename = os.path.join(datadir, 'Contents-i386.gz') while not os.path.exists(filename): time.sleep(1) - print "download complete" - print "starting test ..." - self.filedownloaded = true + print "Download complete" + print "Starting test ..." + self.fileDownloaded = True except KeyboardInterrupt: pass diff --git a/test/test_commands.py b/test/test_commands.py index 5ec04c79b..ca26ceeea 100644 --- a/test/test_commands.py +++ b/test/test_commands.py @@ -88,6 +88,7 @@ class CommandsTestCase(SupyTestCase): def testAny(self): self.assertState([any('int')], ['1', '2', '3'], [[1, 2, 3]]) self.assertState([None, any('int')], ['1', '2', '3'], ['1', [2, 3]]) + self.assertState([any('int')], [], [[]]) ## def testAny(self): ## self.assertState([None, any('int'), None], @@ -104,6 +105,12 @@ class CommandsTestCase(SupyTestCase): self.assertState(spec, ['#foo', '+s'], ['#foo', '+s']) self.assertState(spec, ['+s'], ['#foo', '+s'], target='#foo') + def testGlob(self): + spec = ['glob'] + self.assertState(spec, ['foo'], ['*foo*']) + self.assertState(spec, ['?foo'], ['?foo']) + self.assertState(spec, ['foo*'], ['foo*']) + def testGetId(self): spec = ['id'] self.assertState(spec, ['#12'], [12])