mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-23 02:49:27 +01:00
A bunch more %r -> %s conversions as well as wrap updates, new policy for
Channel.voice and some bug fixes for Debian
This commit is contained in:
parent
2adef7c265
commit
cbd5abbab7
@ -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'<h3>Package ([^<]+)</h3>(.*?)</ul>', _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'<li><a href[^>]+>([^<]+)</a>')
|
||||
def new(self, irc, msg, args, optlist, glob):
|
||||
def new(self, irc, msg, args, section, glob):
|
||||
"""[{main,contrib,non-free}] [<glob>]
|
||||
|
||||
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)
|
||||
|
@ -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):
|
||||
"""[<channel>] [<nick> ...]
|
||||
@ -173,12 +173,21 @@ class Channel(callbacks.Privmsg):
|
||||
voice you. <channel> 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):
|
||||
"""[<channel>] [<nick> ...]
|
||||
@ -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):
|
||||
"""[<channel>] [<nick> ...]
|
||||
@ -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):
|
||||
"""[<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)
|
||||
|
@ -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:
|
||||
|
20
src/Owner.py
20
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):
|
||||
"""[<plugin>] <command>
|
||||
|
||||
|
178
src/User.py
178
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 <capability>] [<glob>]
|
||||
def list(self, irc, msg, args, optlist, glob):
|
||||
"""[--capability=<capability>] [<glob>]
|
||||
|
||||
Returns the valid registered usernames matching <glob>. If <glob> 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] <name> <password>
|
||||
|
||||
Registers <name> with the given password <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):
|
||||
"""<name> [<password>]
|
||||
|
||||
Unregisters <name> 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):
|
||||
"""<name> <new name> [<password>]
|
||||
|
||||
Changes your current user database name to the new name given.
|
||||
@ -167,17 +159,9 @@ class User(callbacks.Privmsg):
|
||||
If you include the <password> 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):
|
||||
"""[<name>] [<hostmask>] [<password>]
|
||||
|
||||
Adds the hostmask <hostmask> to the user specified by <name>. The
|
||||
@ -198,12 +184,8 @@ class User(callbacks.Privmsg):
|
||||
hostmask. If <name> 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):
|
||||
"""<name> <hostmask> [<password>]
|
||||
|
||||
Removes the hostmask <hostmask> from the record of the user specified
|
||||
@ -251,14 +231,6 @@ class User(callbacks.Privmsg):
|
||||
recognized by his hostmask. If you include the <password> 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] <name> <old password> <new password>
|
||||
|
||||
Sets the new password for the user specified by <name> to
|
||||
@ -289,57 +263,36 @@ class User(callbacks.Privmsg):
|
||||
changed isn't that same owner user), then <old password> 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):
|
||||
"""<hostmask|nick>
|
||||
|
||||
Returns the username of the user specified by <hostmask> or <nick> 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):
|
||||
"""[<name>]
|
||||
|
||||
Returns the hostmasks of the user specified by <name>; if <name> 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):
|
||||
"""[<name>]
|
||||
|
||||
Returns the capabilities of the user specified by <name>; if <name>
|
||||
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):
|
||||
"""<name> <password>
|
||||
|
||||
Identifies the user as <name>. 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):
|
||||
"""<password> [<True|False>]
|
||||
|
||||
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] <name> [<value>]
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
13
src/ircdb.py
13
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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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)
|
||||
|
14
src/utils.py
14
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__'])
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
||||
|
@ -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])
|
||||
|
Loading…
Reference in New Issue
Block a user