Merge remote-tracking branch 'supybot/master' into testing

Conflicts:
	.gitignore
	plugins/Alias/plugin.py
	plugins/Anonymous/plugin.py
	plugins/Anonymous/test.py
	setup.py
	src/callbacks.py
This commit is contained in:
Valentin Lorentz 2014-07-12 09:48:27 +02:00
commit 6f19088724
8 changed files with 158 additions and 32 deletions

4
.gitignore vendored
View File

@ -26,3 +26,7 @@ test-logs/
src/version.py src/version.py
INSTALL INSTALL
README.txt README.txt
# Intellij PyCharm / IDEA related files
*.iml
.idea

View File

@ -1,6 +1,6 @@
### ###
# Copyright (c) 2002-2004, Jeremiah Fincher # Copyright (c) 2002-2004, Jeremiah Fincher
# Copyright (c) 2009-2010, James McCoy # Copyright (c) 2014, James McCoy
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@ -81,9 +81,6 @@ def getArgs(args, required=1, optional=0, wildcard=0):
class AliasError(Exception): class AliasError(Exception):
pass pass
class RecursiveAlias(AliasError):
pass
dollarRe = re.compile(r'\$(\d+)') dollarRe = re.compile(r'\$(\d+)')
def findBiggestDollar(alias): def findBiggestDollar(alias):
dollars = dollarRe.findall(alias) dollars = dollarRe.findall(alias)
@ -214,11 +211,11 @@ def makeNewAlias(name, alias):
return True return True
return False return False
everythingReplace(tokens) everythingReplace(tokens)
maxNesting = conf.supybot.commands.nested.maximum() # Limit memory use by constraining the size of the message being passed
if maxNesting and irc.nested+1 > maxNesting: # in to the alias. Also tracking nesting to avoid endless recursion.
irc.error(_('You\'ve attempted more nesting than is ' maxLength = conf.supybot.reply.maximumLength()
'currently allowed on this bot.'), Raise=True) tokens = [t[:maxLength] for t in tokens]
self.Proxy(irc, msg, tokens, nested=irc.nested+1) self.Proxy(irc, msg, tokens, nested=irc.nested + 1)
flexargs = '' flexargs = ''
if biggestDollar and (wildcard or biggestAt): if biggestDollar and (wildcard or biggestAt):
flexargs = _(' at least') flexargs = _(' at least')
@ -343,11 +340,8 @@ class Alias(callbacks.Plugin):
(currentAlias, locked, _) = self.aliases[name] (currentAlias, locked, _) = self.aliases[name]
if locked and currentAlias != alias: if locked and currentAlias != alias:
raise AliasError(format('Alias %q is locked.', name)) raise AliasError(format('Alias %q is locked.', name))
try:
f = makeNewAlias(name, alias) f = makeNewAlias(name, alias)
f = types.MethodType(f, self) f = new.instancemethod(f, self, Alias)
except RecursiveAlias:
raise AliasError('You can\'t define a recursive alias.')
if '.' in name or '|' in name: if '.' in name or '|' in name:
aliasGroup = self.registryValue('escapedaliases', value=False) aliasGroup = self.registryValue('escapedaliases', value=False)
confname = escapeAlias(name) confname = escapeAlias(name)

View File

@ -1,6 +1,6 @@
### ###
# Copyright (c) 2005, Daniel DiPaolo # Copyright (c) 2005, Daniel DiPaolo
# Copyright (c) 2010, James McCoy # Copyright (c) 2014, James McCoy
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@ -78,17 +78,30 @@ class Anonymous(callbacks.Plugin):
@internationalizeDocstring @internationalizeDocstring
def say(self, irc, msg, args, target, text): def say(self, irc, msg, args, target, text):
"""<channel|nick> <text> """<channel> <text>
Sends <text> to <channel|nick>. Can only send to <nick> if Sends <text> to <channel>. Can only send to <nick> if
supybot.plugins.Anonymous.allowPrivateTarget is True. supybot.plugins.Anonymous.allowPrivateTarget is True.
""" """
self._preCheck(irc, msg, target, 'say') self._preCheck(irc, msg, target, 'say')
self.log.info('Saying %q to %s due to %s.', self.log.info('Saying %q in %s due to %s.',
text, target, msg.prefix) text, target, msg.prefix)
irc.reply(text, to=target, prefixNick=False, irc.queueMsg(ircmsgs.privmsg(target, text))
private=not ircutils.isChannel(target)) irc.noReply()
say = wrap(say, [first('nick', 'inChannel'), 'text']) say = wrap(say, ['inChannel', 'text'])
def tell(self, irc, msg, args, target, text):
"""<nick> <text>
Sends <text> to <nick>. Can only be used if
supybot.plugins.Anonymous.allowPrivateTarget is True.
"""
self._preCheck(irc, msg, target, 'tell')
self.log.info('Telling %q to %s due to %s.',
text, target, msg.prefix)
irc.queueMsg(ircmsgs.privmsg(target, text))
irc.noReply()
tell = wrap(tell, ['nick', 'text'])
@internationalizeDocstring @internationalizeDocstring
def do(self, irc, msg, args, channel, text): def do(self, irc, msg, args, channel, text):

View File

@ -1,6 +1,6 @@
### ###
# Copyright (c) 2005, Daniel DiPaolo # Copyright (c) 2005, Daniel DiPaolo
# Copyright (c) 2010, James McCoy # Copyright (c) 2014, James McCoy
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@ -34,16 +34,24 @@ class AnonymousTestCase(ChannelPluginTestCase):
plugins = ('Anonymous',) plugins = ('Anonymous',)
def testSay(self): def testSay(self):
self.assertError('anonymous say %s I love you!' % self.channel) self.assertError('anonymous say %s I love you!' % self.channel)
self.assertError('anonymous say %s I love you!' % self.nick) origreg = conf.supybot.plugins.Anonymous.requireRegistration()
try:
conf.supybot.plugins.Anonymous.requireRegistration.setValue(False)
m = self.assertNotError('anonymous say %s foo!' % self.channel)
self.failUnless(m.args[1] == 'foo!')
finally:
conf.supybot.plugins.Anonymous.requireRegistration.setValue(origreg)
def testTell(self):
self.assertError('anonymous tell %s I love you!' % self.nick)
origreg = conf.supybot.plugins.Anonymous.requireRegistration() origreg = conf.supybot.plugins.Anonymous.requireRegistration()
origpriv = conf.supybot.plugins.Anonymous.allowPrivateTarget() origpriv = conf.supybot.plugins.Anonymous.allowPrivateTarget()
try: try:
conf.supybot.plugins.Anonymous.requireRegistration.setValue(False) conf.supybot.plugins.Anonymous.requireRegistration.setValue(False)
m = self.assertNotError('anonymous say %s foo!' % self.channel) self.assertError('anonymous tell %s foo!' % self.channel)
self.assertEqual(m.args[1], 'foo!')
conf.supybot.plugins.Anonymous.allowPrivateTarget.setValue(True) conf.supybot.plugins.Anonymous.allowPrivateTarget.setValue(True)
m = self.assertNotError('anonymous say %s foo!' % self.nick) m = self.assertNotError('anonymous tell %s foo!' % self.nick)
self.assertEqual(m.args[1], 'foo!') self.failUnless(m.args[1] == 'foo!')
finally: finally:
conf.supybot.plugins.Anonymous.requireRegistration.setValue(origreg) conf.supybot.plugins.Anonymous.requireRegistration.setValue(origreg)
conf.supybot.plugins.Anonymous.allowPrivateTarget.setValue(origpriv) conf.supybot.plugins.Anonymous.allowPrivateTarget.setValue(origpriv)

View File

@ -186,6 +186,3 @@ if __name__ == '__main__':
# 'test-data', 'test-logs', 'tmp') # 'test-data', 'test-logs', 'tmp')
# for fn in configFiles: # for fn in configFiles:
# os.remove(fn) # os.remove(fn)
# This is the part where we do our release on Freshmeat using XMLRPC and
# <gasp> ESR's software to do it: http://freshmeat.net/p/freshmeat-submit/

View File

@ -274,6 +274,16 @@ setup(
('share/man/man1', ['docs/man/supybot-adduser.1']), ('share/man/man1', ['docs/man/supybot-adduser.1']),
('share/man/man1', ['docs/man/supybot-plugin-doc.1']), ('share/man/man1', ['docs/man/supybot-plugin-doc.1']),
('share/man/man1', ['docs/man/supybot-plugin-create.1']), ('share/man/man1', ['docs/man/supybot-plugin-create.1']),
],
install_requires=[
# Time plugin
'python-dateutil <2.0,>=1.3',
'feedparser',
],
tests_require=[
'mock',
] ]
) )

View File

@ -1,7 +1,7 @@
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
### ###
# Copyright (c) 2002-2005, Jeremiah Fincher # Copyright (c) 2002-2005, Jeremiah Fincher
# Copyright (c) 2008-2010, James McCoy # Copyright (c) 2014, James McCoy
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@ -642,6 +642,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
self.msg.prefix, maxNesting) self.msg.prefix, maxNesting)
self.error(_('You\'ve attempted more nesting than is ' self.error(_('You\'ve attempted more nesting than is '
'currently allowed on this bot.')) 'currently allowed on this bot.'))
return
# The deepcopy here is necessary for Scheduler; it re-runs already # The deepcopy here is necessary for Scheduler; it re-runs already
# tokenized commands. There's a possibility a simple copy[:] would # tokenized commands. There's a possibility a simple copy[:] would
# work, but we're being careful. # work, but we're being careful.

99
test/test_yn.py Normal file
View File

@ -0,0 +1,99 @@
###
# Copyright (c) 2014, Artur Krysiak
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
from supybot import questions
from supybot.test import SupyTestCase
import mock
# so complicated construction because I want to
# gain the string 'y' instead of the character 'y'
# the reason of usage this construction is to prove
# that comparing strings by 'is' is wrong
# better solution is usage of '==' operator ;)
_yes_answer = ''.join(['', 'y'])
class TestYn(SupyTestCase):
def test_default_yes_selected(self):
questions.expect = mock.Mock(return_value=_yes_answer)
answer = questions.yn('up', default='y')
self.assertTrue(answer)
def test_default_no_selected(self):
questions.expect = mock.Mock(return_value='n')
answer = questions.yn('up', default='n')
self.assertFalse(answer)
def test_yes_selected_without_defaults(self):
questions.expect = mock.Mock(return_value=_yes_answer)
answer = questions.yn('up')
self.assertTrue(answer)
def test_no_selected_without_defaults(self):
questions.expect = mock.Mock(return_value='n')
answer = questions.yn('up')
self.assertFalse(answer)
def test_no_selected_with_default_yes(self):
questions.expect = mock.Mock(return_value='n')
answer = questions.yn('up', default='y')
self.assertFalse(answer)
def test_yes_selected_with_default_yes(self):
questions.expect = mock.Mock(return_value=_yes_answer)
answer = questions.yn('up', default='y')
self.assertTrue(answer)
def test_yes_selected_with_default_no(self):
questions.expect = mock.Mock(return_value=_yes_answer)
answer = questions.yn('up', default='n')
self.assertTrue(answer)
def test_no_selected_with_default_no(self):
questions.expect = mock.Mock(return_value='n')
answer = questions.yn('up', default='n')
self.assertFalse(answer)
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: