Merge remote-tracking branch 'progval/testing' into testing

This commit is contained in:
nyuszika7h 2011-08-13 13:45:02 +02:00
commit 9477ba20fb
15 changed files with 194 additions and 65 deletions

View File

@ -34,7 +34,7 @@ import supybot.conf as conf
class ConfigTestCase(ChannelPluginTestCase):
# We add utilities so there's something in supybot.plugins.
plugins = ('Config', 'Utilities', 'AutoMode')
plugins = ('Config', 'Utilities')
def testGet(self):
self.assertNotRegexp('config get supybot.reply', r'registry\.Group')
self.assertResponse('config supybot.protocols.irc.throttleTime', '0.0')
@ -49,9 +49,6 @@ class ConfigTestCase(ChannelPluginTestCase):
def testHelp(self):
self.assertError('config help alsdkfj')
self.assertError('config help supybot.alsdkfj')
self.assertNotRegexp('config list supybot', '.*alsdkfj.*')
self.assertError('config help supybot.plugins.AutoMode.ban.alsdkfj')
self.assertNotRegexp('config list supybot.plugins.AutoMode.ban', '.*alsdkfj.*')
self.assertNotError('config help supybot') # We tell the user to list.
self.assertNotError('config help supybot.plugins')
self.assertNotError('config help supybot.replies.success')

View File

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: 2011-06-26 18:57+0200\n"
"PO-Revision-Date: 2011-08-10 14:27+0200\n"
"Last-Translator: skizzhg <skizzhg@gmx.com>\n"
"Language-Team: Italian <skizzhg@gmx.com>\n"
"Language: it\n"
@ -159,7 +159,28 @@ msgstr ""
msgid "There were no notes for %r"
msgstr "Non ci sono note per %r"
#: plugin.py:231
#: plugin.py:212
#, docstring
msgid ""
"<nick>\n"
"\n"
" Removes the latest note you sent to <nick>.\n"
" "
msgstr ""
"<nick>\n"
"\n"
" Rimuove l'ultima nota inviata a <nick>.\n"
" "
#: plugin.py:217
msgid "There are no note waiting for %s."
msgstr "Non ci sono note in attesa per %s."
#: plugin.py:228
msgid "There are no note from you waiting for %s."
msgstr "Non ci sono note in attesa per %s da te inviate."
#: plugin.py:252
msgid "Sent %s: <%s> %s"
msgstr "Inviata %s: <%s> %s"

View File

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: 2011-08-10 02:11+0200\n"
"PO-Revision-Date: 2011-08-10 14:39+0200\n"
"Last-Translator: skizzhg <skizzhg@gmx.com>\n"
"Language-Team: Italian <skizzhg@gmx.com>\n"
"Language: it\n"
@ -161,12 +161,12 @@ msgstr "La versione di questo Supybot è %s. %s"
msgid ""
"takes no arguments\n"
"\n"
" Returns a URL saying where to get Supybot.\n"
" Returns a URL saying where to get Limnoria.\n"
" "
msgstr ""
"non necessita argomenti\n"
"\n"
" Riporta un URL che dice dove ottenere Supybot.\n"
" Riporta un URL che dice dove ottenere Limnoria.\n"
" "
#: plugin.py:243
@ -274,7 +274,7 @@ msgstr "Non ho mai visto %s, lascio a te l'invio del messaggio."
msgid "%s wants me to tell you: %s"
msgstr "%s vuole che ti dica: %s"
#: plugin.py:430
#: plugin.py:431
#, docstring
msgid ""
"takes no arguments\n"
@ -287,7 +287,7 @@ msgstr ""
" Controlla che il bot sia ancora vivo.\n"
" "
#: plugin.py:434
#: plugin.py:435
msgid "pong"
msgstr "pong"

View File

@ -43,10 +43,15 @@ import supybot.irclib as irclib
import supybot.ircmsgs as ircmsgs
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from supybot import commands
from supybot.utils.iter import ifilter
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('Misc')
class RegexpTimeout(Exception):
pass
class Misc(callbacks.Plugin):
def __init__(self, irc):
self.__parent = super(Misc, self)
@ -332,10 +337,27 @@ class Misc(callbacks.Plugin):
predicates.setdefault('without', []).append(f)
elif option == 'regexp':
def f(m, arg=arg):
def f1(s, arg):
"""Since we can't enqueue match objects into the multiprocessing queue,
we'll just wrap the function to return bools."""
if arg.search(s) is not None:
return True
else:
return False
if ircmsgs.isAction(m):
return arg.search(ircmsgs.unAction(m))
m1 = ircmsgs.unAction(m)
#return arg.search(ircmsgs.unAction(m))
else:
return arg.search(m.args[1])
m1 = m.args[1]
#return arg.search(m.args[1])
try:
# use a subprocess here, since specially crafted regexps can
# take exponential time and hang up the bot.
# timeout of 0.1 should be more than enough for any normal regexp.
v = commands.process(f1, m1, arg, timeout=0.1, pn=self.name(), cn='last')
return v
except commands.ProcessTimeoutError:
return False
predicates.setdefault('regexp', []).append(f)
elif option == 'nolimit':
nolimit = True
@ -370,8 +392,12 @@ class Misc(callbacks.Plugin):
showNick = True
for m in iterable:
for predicate in predicates:
if not predicate(m):
break
try:
if not predicate(m):
break
except RegexpTimeout:
irc.error(_('The regular expression timed out.'))
return
else:
if nolimit:
resp.append(ircmsgs.prettyPrint(m,

View File

@ -42,6 +42,7 @@ import supybot.ircmsgs as ircmsgs
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from supybot import commands
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('Note')
@ -294,7 +295,8 @@ class Note(callbacks.Plugin):
own = to
for (option, arg) in optlist:
if option == 'regexp':
criteria.append(arg.search)
criteria.append(lambda x: commands.regexp_wrapper(x, reobj=arg,
timeout=0.1, plugin_name = self.name(), fcn_name='search'))
elif option == 'sent':
own = frm
if glob:

View File

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: 2011-08-10 02:27+0200\n"
"PO-Revision-Date: 2011-08-10 14:43+0200\n"
"Last-Translator: skizzhg <skizzhg@gmx.com>\n"
"Language-Team: Italian <skizzhg@gmx.com>\n"
"Language: it\n"
@ -10,47 +10,48 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: plugin.py:203 plugin.py:284
#: plugin.py:204 plugin.py:285
msgid "%s was last seen in %s %s ago: %s"
msgstr "%s è stato visto per l'ultima volta in %s %s fa: %s"
#: plugin.py:210
#: plugin.py:211
msgid "%s (%s ago)"
msgstr "%s (%s fa)"
#: plugin.py:212
#: plugin.py:213
msgid "%s could be %L"
msgstr "%s potrebbe essere %L"
#: plugin.py:212
#: plugin.py:213
msgid "or"
msgstr "oppure"
#: plugin.py:214
#: plugin.py:215
msgid "I haven't seen anyone matching %s."
msgstr "Non ho visto nessuno che corrisponda a %s."
#: plugin.py:216 plugin.py:288
#: plugin.py:217 plugin.py:289
msgid "I have not seen %s."
msgstr "Non ho visto %s."
#: plugin.py:220
#: plugin.py:221
#, docstring
msgid ""
"[<channel>] <nick>\n"
"\n"
" Returns the last time <nick> was seen and what <nick> was last seen\n"
" saying. <channel> is only necessary if the message isn't sent on the\n"
" channel itself.\n"
" channel itself. <nick> may contain * as a wildcard.\n"
" "
msgstr ""
"[<canale>] <nick>\n"
"\n"
" Riporta l'ultima volta che <nick> è stato visto e cosa stava dicendo.\n"
" <canale> è necessario solo se il messaggio non viene inviato nel canale stesso.\n"
" <canale> è necessario solo se il messaggio non viene inviato nel canale\n"
" stesso. <nick> può contenere * come wildcard.\n"
" "
#: plugin.py:231
#: plugin.py:232
#, docstring
msgid ""
"[<channel>] [--user <name>] [<nick>]\n"
@ -73,15 +74,15 @@ msgstr ""
" solo se il messaggio non viene inviato nel canale stesso.\n"
" "
#: plugin.py:261
#: plugin.py:262
msgid "Someone was last seen in %s %s ago: %s"
msgstr "Qualcuno è stato visto per l'ultima volta in %s %s fa: %s"
#: plugin.py:265
#: plugin.py:266
msgid "I have never seen anyone."
msgstr "Non ho mai visto nessuno."
#: plugin.py:269
#: plugin.py:270
#, docstring
msgid ""
"[<channel>]\n"
@ -96,15 +97,15 @@ msgstr ""
" solo se il messaggio non viene inviato nel canale stesso.\n"
" "
#: plugin.py:292
#: plugin.py:293
#, docstring
msgid ""
"[<canale>] <name>\n"
"[<channel>] <name>\n"
"\n"
" Returns the last time <name> was seen and what <name> was last seen\n"
" saying. This looks up <name> in the user seen database, which means\n"
" that it could be any nick recognized as user <name> that was seen.\n"
" <canale> is only necessary if the message isn't sent in the channel\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
@ -116,7 +117,7 @@ msgstr ""
" <canale> è necessario solo se il messaggio non viene inviato nel canale stesso.\n"
" "
#: plugin.py:305
#: plugin.py:306
#, docstring
msgid ""
"[<channel>] <nick>\n"
@ -129,15 +130,15 @@ msgstr ""
" Riporta i messaggi da quando <nick> ha lasciato il canale l'ultima volta.\n"
" "
#: plugin.py:312
#: plugin.py:313
msgid "You must be in %s to use this command."
msgstr "Per usare questo comando bisogna essere in %s."
#: plugin.py:333
#: plugin.py:334
msgid "I couldn't find in my history of %s messages where %r last left the %s"
msgstr "Non trovo nella cronologia dei messaggi di %s dove %r ha lasciato %s l'ultima volta."
#: plugin.py:342
#: plugin.py:343
msgid "Either %s didn't leave, or no messages were sent while %s was gone."
msgstr "%s non è uscito o non ha inviato alcun messaggio quando se n'é andato."

View File

@ -53,4 +53,11 @@ conf.registerGlobalValue(String.levenshtein, 'max',
this variable, to limit the size of arguments passed to the levenshtein
command.""")))
conf.registerGroup(String, 're')
conf.registerGlobalValue(String.re, 'timeout',
registry.PositiveFloat(0.1, """Determines the maximum time, in seconds, that
a regular expression is given to execute before being terminated. Since
there is a possibility that user input for the re command can cause it to
eat up large amounts of ram or cpu time, it's a good idea to keep this
low. Most normal regexps should not take very long at all."""))
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -146,10 +146,14 @@ class String(callbacks.Plugin):
s = _('You probably don\'t want to match the empty string.')
irc.error(s)
else:
irc.reply(f(text))
re = wrap(re, [('checkCapability', 'trusted'),
first('regexpMatcher', 'regexpReplacer'),
'text'])
t = self.registryValue('re.timeout')
try:
v = commands.process(f, text, timeout=t, pn=self.name(), cn='re')
irc.reply(v)
except commands.ProcessTimeoutError, e:
irc.error("ProcessTimeoutError: %s" % (e,))
re = thread(wrap(re, [first('regexpMatcher', 'regexpReplacer'),
'text']))
@internationalizeDocstring
def xor(self, irc, msg, args, password, text):

View File

@ -171,8 +171,6 @@ class Time(callbacks.Plugin):
except ImportError:
irc.error(_('Python-tz is required by the command, but is not '
'installed on this computer.'))
if len(timezone.split('/')) != 2:
irc.error(_('A timezone must be in the format region/city.'))
try:
timezone = pytz.timezone(timezone)
except pytz.UnknownTimeZoneError:

View File

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: 2011-08-10 02:33+0200\n"
"PO-Revision-Date: 2011-08-10 14:53+0200\n"
"Last-Translator: skizzhg <skizzhg@gmx.com>\n"
"Language-Team: Italian <skizzhg@gmx.com>\n"
"Language: it\n"
@ -10,6 +10,14 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: config.py:50
msgid ""
"Determines whether users can read the\n"
" todo-list of another user."
msgstr ""
"Determina se un utente possa leggere la lista delle cose da fare di un altro utente."
#: plugin.py:135
#, docstring
msgid ""
@ -27,43 +35,47 @@ msgstr ""
" ha aggiunto alla sua lista delle cose da fare.\n"
" "
#: plugin.py:150
#: plugin.py:146
msgid "You are not allowed to see other users todo-list."
msgstr "Non hai l'autorizzazione pe rleggere la lista delle cose da fare degli altri utenti."
#: plugin.py:153
msgid "#%i: %s"
msgstr "#%i: %s"
#: plugin.py:155
#: plugin.py:158
msgid "%s for %s: %L"
msgstr "%s per %s: %L"
#: plugin.py:159
#: plugin.py:162
msgid "That user has no tasks in their todo list."
msgstr "Questo utente non ha compiti nella sua lista delle cose da fare."
#: plugin.py:161
#: plugin.py:164
msgid "You have no tasks in your todo list."
msgstr "Non hai compiti nella tua lista delle cose da fare."
#: plugin.py:168
#: plugin.py:171
msgid "Active"
msgstr "Attivo"
#: plugin.py:170
#: plugin.py:173
msgid "Inactive"
msgstr "Inattivo"
#: plugin.py:172
#: plugin.py:175
msgid ", priority: %i"
msgstr ", priorità: %i"
#: plugin.py:175
#: plugin.py:178
msgid "%s todo for %s: %s (Added at %s)"
msgstr "%s compito per %s: %s (Aggiunto il %s)"
#: plugin.py:179 plugin.py:260 plugin.py:274
#: plugin.py:182 plugin.py:263 plugin.py:277
msgid "task id"
msgstr "id compito"
#: plugin.py:184
#: plugin.py:187
#, docstring
msgid ""
"[--priority=<num>] <text>\n"
@ -80,11 +92,11 @@ msgstr ""
" Ogni numero intero è valido.\n"
" "
#: plugin.py:195
#: plugin.py:198
msgid "(Todo #%i added)"
msgstr "(Compito #%i aggiunto)"
#: plugin.py:201
#: plugin.py:204
#, docstring
msgid ""
"<task id> [<task id> ...]\n"
@ -97,15 +109,15 @@ msgstr ""
" Rimuove <id compito> dalla lista personale delle cose da fare.\n"
" "
#: plugin.py:212
#: plugin.py:215
msgid "Task %i could not be removed either because that id doesn't exist or it has been removed already."
msgstr "Il compito %i non può essere rimosso in quanto l'id non esiste o è già stato rimosso."
#: plugin.py:216
#: plugin.py:219
msgid "No tasks were removed because the following tasks could not be removed: %L."
msgstr "Non è stato rimosso nessun compito perché i seguenti non possono essere rimossi: %L."
#: plugin.py:226
#: plugin.py:229
#, docstring
msgid ""
"[--{regexp} <value>] [<glob> <glob> ...]\n"
@ -121,11 +133,11 @@ msgstr ""
" Se --regexp è fornita, il suo valore associato è usato come regexp e confrontato con i compiti.\n"
" "
#: plugin.py:246
#: plugin.py:249
msgid "No tasks matched that query."
msgstr "Nessun compito corrisponde alla richiesta."
#: plugin.py:252
#: plugin.py:255
#, docstring
msgid ""
"<id> <priority>\n"
@ -138,7 +150,7 @@ msgstr ""
" Imposta la priorità del compito con l'id fornito al valore specificato.\n"
" "
#: plugin.py:266
#: plugin.py:269
#, docstring
msgid ""
"<task id> <regexp>\n"

View File

@ -41,6 +41,7 @@ from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from supybot import commands
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('Todo')
@ -237,6 +238,8 @@ class Todo(callbacks.Plugin):
criteria = []
for (option, arg) in optlist:
if option == 'regexp':
criteria.append(lambda x: commands.regexp_wrapper(x, reobj=arg,
timeout=0.1, plugin_name = self.name(), fcn_name='search'))
criteria.append(arg.search)
for glob in globs:
glob = utils.python.glob2re(glob)

View File

@ -50,6 +50,7 @@ import supybot.world as world
from supybot.commands import *
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from supybot import commands
## i think we don't need any of this with sqlite3
#try:
@ -431,7 +432,9 @@ class ChannelIdDatabasePlugin(callbacks.Plugin):
if opt == 'by':
predicates.append(lambda r, arg=arg: r.by == arg.id)
elif opt == 'regexp':
predicates.append(lambda r, arg=arg: arg.search(r.text))
predicates.append(lambda x: commands.regexp_wrapper(x.text, reobj=arg,
timeout=0.1, plugin_name = self.name(), fcn_name='search'))
#predicates.append(lambda r, arg=arg: arg.search(r.text))
if glob:
def globP(r, glob=glob.lower()):
return fnmatch.fnmatch(r.text.lower(), glob)

View File

@ -69,6 +69,61 @@ def thread(f):
f(self, irc, msg, args, *L, **kwargs)
return utils.python.changeFunctionName(newf, f.func_name, f.__doc__)
class ProcessTimeoutError(Exception):
"""Gets raised when a process is killed due to timeout."""
pass
def process(f, *args, **kwargs):
"""Runs a function <f> in a subprocess.
Several extra keyword arguments can be supplied.
<pn>, the pluginname, and <cn>, the command name, are strings used to
create the process name, for identification purposes.
<timeout>, if supplied, limits the length of execution of target
function to <timeout> seconds."""
timeout = kwargs.pop('timeout', None)
q = multiprocessing.Queue()
def newf(f, q, *args, **kwargs):
try:
r = f(*args, **kwargs)
q.put(r)
except Exception as e:
q.put(e)
targetArgs = (f, q,) + args
p = callbacks.CommandProcess(target=newf,
args=targetArgs, kwargs=kwargs)
p.start()
p.join(timeout)
if p.is_alive():
p.terminate()
raise ProcessTimeoutError, "%s aborted due to timeout." % (p.name,)
try:
v = q.get(block=False)
except Queue.Empty:
v = "Nothing returned."
if isinstance(v, Exception):
v = "Error: " + str(v)
return v
def regexp_wrapper(s, reobj, timeout, plugin_name, fcn_name):
'''A convenient wrapper to stuff regexp search queries through a subprocess.
This is used because specially-crafted regexps can use exponential time
and hang the bot.'''
def re_bool(s, reobj):
"""Since we can't enqueue match objects into the multiprocessing queue,
we'll just wrap the function to return bools."""
if reobj.search(s) is not None:
return True
else:
return False
try:
v = process(re_bool, s, reobj, timeout=timeout, pn=plugin_name, cn=fcn_name)
return v
except ProcessTimeoutError:
return False
class UrlSnarfThread(world.SupyThread):
def __init__(self, *args, **kwargs):
assert 'url' in kwargs

View File

@ -206,7 +206,7 @@ class Group(object):
def __getattr__(self, attr):
if attr in self._children:
return self._children[attr]
elif self._supplyDefault and attr.startswith('#'):
elif self._supplyDefault:
return self.__makeChild(attr, str(self))
else:
self.__nonExistentEntry(attr)

View File

@ -1,3 +1,3 @@
"""stick the various versioning attributes in here, so we only have to change
them once."""
version = '0.83.4.1+limnoria (2011-08-10T12:00:42+0200)'
version = '0.83.4.1+limnoria (2011-08-13T01:59:18+0200)'