diff -Nru limnoria-2018.12.19/debian/changelog limnoria-2019.02.23/debian/changelog --- limnoria-2018.12.19/debian/changelog 2019-01-02 09:55:17.000000000 +0000 +++ limnoria-2019.02.23/debian/changelog 2019-02-23 00:52:11.000000000 +0000 @@ -1,3 +1,10 @@ +limnoria (2019.02.23-1) unstable; urgency=medium + + * New upstream version 2019.02.23 + * d/t/upstream-tests: Remove 'unix call' test workaround, fixed upstream. + + -- Unit 193 Fri, 22 Feb 2019 19:52:11 -0500 + limnoria (2018.12.19-1) unstable; urgency=medium * d/t/upstream-tests: Run tests against all python versions. diff -Nru limnoria-2018.12.19/debian/tests/upstream-tests limnoria-2019.02.23/debian/tests/upstream-tests --- limnoria-2018.12.19/debian/tests/upstream-tests 2018-10-04 23:59:43.000000000 +0000 +++ limnoria-2019.02.23/debian/tests/upstream-tests 2019-02-23 00:50:49.000000000 +0000 @@ -11,11 +11,6 @@ cd $AUTOPKGTEST_TMP -# plugins.Unix.test.UnixTestCase.testCall checks whether there is a 'src' in . -# but we're running the testsuite out of the source tree, so let's fake a 'src' -# thing in the current directory. -touch src - for python in $(py3versions --supported);do printf "###\\n### Now testing %s\\n###\\n" "$python" $python ./supybot-test test \ diff -Nru limnoria-2018.12.19/LICENSE.md limnoria-2019.02.23/LICENSE.md --- limnoria-2018.12.19/LICENSE.md 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2019.02.23/LICENSE.md 2019-02-21 20:54:16.000000000 +0000 @@ -0,0 +1,28 @@ +Copyright (c) 2002-2009 Jeremiah Fincher and others +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 permission. + +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. + +Portions of the included source code are copyright by its original author(s) +and remain subject to its associated license. diff -Nru limnoria-2018.12.19/Makefile limnoria-2019.02.23/Makefile --- limnoria-2018.12.19/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2019.02.23/Makefile 2019-02-21 20:54:16.000000000 +0000 @@ -0,0 +1,42 @@ +PYTHON=`which python3` +DESTDIR=/ +PROJECT=limnoria + +all: + @echo "make source - Create source package" + @echo "make install - Install on local system" + @echo "make buildrpm - Generate a rpm package" + @echo "make builddeb_py2 - Generate a deb package for Python 2" + @echo "make builddeb_py3 - Generate a deb package for Python 3" + @echo "make clean - Get rid of scratch and byte files" + +test: + PATH=./scripts/:${PATH} PYTHONPATH=. $(PYTHON) ./scripts/supybot-test test --plugins-dir=plugins/ + +source: + $(PYTHON) setup.py sdist $(COMPILE) + +install: + $(PYTHON) setup.py install --root $(DESTDIR) $(COMPILE) + +buildrpm: + $(PYTHON) setup.py bdist_rpm + +builddeb_py2: + cp debian/control.py2 debian/control + debuild -us -uc + rm debian/control + +builddeb_py3: + cp debian/control.py3 debian/control + debuild -us -uc + rm debian/control + +clean: + $(PYTHON) setup.py clean + $(MAKE) -f $(CURDIR)/debian/rules clean + rm -rf build/ MANIFEST + find . -name '*.pyc' -delete + rm debian/control + +.PHONY: test diff -Nru limnoria-2018.12.19/PKG-INFO limnoria-2019.02.23/PKG-INFO --- limnoria-2018.12.19/PKG-INFO 2018-12-24 17:21:57.000000000 +0000 +++ limnoria-2019.02.23/PKG-INFO 2019-02-22 23:11:30.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: limnoria -Version: 2018.12.19 +Version: 2019.02.23 Summary: A modified version of Supybot (an IRC bot and framework) Home-page: https://github.com/ProgVal/Limnoria Author: Valentin Lorentz diff -Nru limnoria-2018.12.19/plugins/ChannelStats/plugin.py limnoria-2019.02.23/plugins/ChannelStats/plugin.py --- limnoria-2018.12.19/plugins/ChannelStats/plugin.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/plugins/ChannelStats/plugin.py 2019-02-21 20:54:16.000000000 +0000 @@ -292,7 +292,7 @@ name, channel)) stats = wrap(stats, ['channeldb', additional('something')]) - _calc_match_forbidden_chars = re.compile('[_[\]]') + _calc_match_forbidden_chars = re.compile('[_\[\]]') _env = {'__builtins__': types.ModuleType('__builtins__')} _env.update(math.__dict__) def _factorial(x): diff -Nru limnoria-2018.12.19/plugins/Math/plugin.py limnoria-2019.02.23/plugins/Math/plugin.py --- limnoria-2018.12.19/plugins/Math/plugin.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/plugins/Math/plugin.py 2019-02-21 20:54:16.000000000 +0000 @@ -157,7 +157,7 @@ else: return '%s%s' % (realS, imagS) - _calc_match_forbidden_chars = re.compile('[_[\]]') + _calc_match_forbidden_chars = re.compile('[_\[\]]') _calc_remover = utils.str.MultipleRemover('_[] \t') ### # So this is how the 'calc' command works: diff -Nru limnoria-2018.12.19/plugins/Nickometer/plugin.py limnoria-2019.02.23/plugins/Nickometer/plugin.py --- limnoria-2018.12.19/plugins/Nickometer/plugin.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/plugins/Nickometer/plugin.py 2019-02-21 20:54:16.000000000 +0000 @@ -151,7 +151,7 @@ nickInitial = nick nick=re.sub('^([^()]*)(\()(.*)(\))([^()]*)$', '\1\3\5', nick, 1) nick=re.sub('^([^{}]*)(\{)(.*)(\})([^{}]*)$', '\1\3\5', nick, 1) - nick=re.sub('^([^[\]]*)(\[)(.*)(\])([^[\]]*)$', '\1\3\5', nick, 1) + nick=re.sub('^([^\[\]]*)(\[)(.*)(\])([^\[\]]*)$', '\1\3\5', nick, 1) if nick == nickInitial: break self.log.debug('Removed some matching brackets %r => %r', diff -Nru limnoria-2018.12.19/plugins/PluginDownloader/plugin.py limnoria-2019.02.23/plugins/PluginDownloader/plugin.py --- limnoria-2018.12.19/plugins/PluginDownloader/plugin.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/plugins/PluginDownloader/plugin.py 2019-02-21 20:54:16.000000000 +0000 @@ -310,15 +310,18 @@ 't3chguy', 'Limnoria-Plugins', ), - 'prgmrbill': GithubRepository( + 'prgmrbill': GithubRepository( 'prgmrbill', 'limnoria-plugins', ), - 'fudster': GithubRepository( + 'fudster': GithubRepository( 'fudster', 'supybot-plugins', ), - + 'oddluck': GithubRepository( + 'oddluck', + 'limnoria-plugins', + ), }) class PluginDownloader(callbacks.Plugin): diff -Nru limnoria-2018.12.19/plugins/Web/plugin.py limnoria-2019.02.23/plugins/Web/plugin.py --- limnoria-2018.12.19/plugins/Web/plugin.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/plugins/Web/plugin.py 2019-02-21 20:54:16.000000000 +0000 @@ -156,14 +156,20 @@ 'replace') except UnicodeDecodeError: pass - parser = Title() if minisix.PY3 and isinstance(text, bytes): if raiseErrors: irc.error(_('Could not guess the page\'s encoding. (Try ' 'installing python-charade.)'), Raise=True) else: return None - parser.feed(text) + try: + parser = Title() + parser.feed(text) + except UnicodeDecodeError: + # Workaround for Python 2 + # https://github.com/ProgVal/Limnoria/issues/1359 + parser = Title() + parser.feed(text.encode('utf8')) parser.close() title = utils.str.normalizeWhitespace(''.join(parser.data).strip()) if title: diff -Nru limnoria-2018.12.19/requirements.txt limnoria-2019.02.23/requirements.txt --- limnoria-2018.12.19/requirements.txt 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2019.02.23/requirements.txt 2019-02-21 20:54:16.000000000 +0000 @@ -0,0 +1,9 @@ +chardet +pytz +python-dateutil +python-gnupg +feedparser +sqlalchemy +PySocks +mock +ecdsa diff -Nru limnoria-2018.12.19/scripts/supybot-reset-password limnoria-2019.02.23/scripts/supybot-reset-password --- limnoria-2018.12.19/scripts/supybot-reset-password 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2019.02.23/scripts/supybot-reset-password 2019-02-21 20:54:16.000000000 +0000 @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +### +# Copyright (c) 2002-2004, Jeremiah Fincher +# 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. +### + + + +import supybot + +from supybot.questions import * + +import os +import sys +import optparse + +def main(): + import supybot.log as log + import supybot.conf as conf + conf.supybot.log.stdout.setValue(False) + parser = optparse.OptionParser(usage='Usage: %prog [options] ', + version='supybot %s' % conf.version) + parser.add_option('-u', '--username', action='store', default='', + dest='name', + help='username for the user.') + parser.add_option('-p', '--password', action='store', default='', + dest='password', + help='password for the user.') + (options, args) = parser.parse_args() + if len(args) is not 1: + parser.error('Specify the users.conf file you\'d like to use. ' + 'Be sure *not* to specify your registry file, generated ' + 'by supybot-wizard. This is not the file you want. ' + 'Instead, take a look in your conf directory (usually ' + 'named "conf") and take a gander at the file ' + '"users.conf". That\'s the one you want.') + + filename = os.path.abspath(args[0]) + conf.supybot.directories.log.setValue('/') + conf.supybot.directories.conf.setValue('/') + conf.supybot.directories.data.setValue('/') + conf.supybot.directories.plugins.setValue(['/']) + conf.supybot.databases.users.filename.setValue(filename) + import supybot.ircdb as ircdb + + if not options.name: + name = '' + while not name: + name = something('What is the user\'s name?') + try: + # Check to see if the user is already in the database. + _ = ircdb.users.getUser(name) + # Success! + except KeyError: + # Failure. No such user exists. Try another name. + output('That user doesn\'t exist. Try another name.') + name = '' + else: + try: + _ = ircdb.users.getUser(options.name) + name = options.name + except KeyError: + # Same as above. We exit here instead. + output('That user doesn\'t exist. Try another name.') + sys.exit(-1) + + if not options.password: + password = getpass('Please enter new password for %s: ' % name) + else: + password = options.password + + user = ircdb.users.getUser(name) + user.setPassword(password) + ircdb.users.setUser(user) + ircdb.users.flush() + ircdb.users.close() + print('User %s\'s password reset!' % name) + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2018.12.19/setup.py limnoria-2019.02.23/setup.py --- limnoria-2018.12.19/setup.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/setup.py 2019-02-22 23:11:08.000000000 +0000 @@ -67,6 +67,7 @@ from src.version import version else: version = 'installed on ' + time.strftime("%Y-%m-%dT%H-%M-%S", time.gmtime()) +version = '2019.02.23' try: os.unlink(VERSION_FILE) except OSError: # Does not exist @@ -219,6 +220,7 @@ 'scripts/supybot-botchk', 'scripts/supybot-wizard', 'scripts/supybot-adduser', + 'scripts/supybot-reset-password', 'scripts/supybot-plugin-doc', 'scripts/supybot-plugin-create', ], @@ -240,7 +242,7 @@ '', DeprecationWarning) elif sys.version_info < (3, 0): - pass # fine, for the moment + pass elif sys.version_info < (3, 4): warnings.warn('Running Limnoria on Python 3.2 or 3.3 is not ' 'recommended because these versions do not support SSL ' @@ -248,4 +250,13 @@ '', DeprecationWarning) +if sys.version_info < (3, 0): + warnings.warn('You are installing Limnoria for Python 2. While Python 2 ' + 'is currently supported by Limnoria, this may change in the near ' + 'future. If there is anything preventing you frorm upgrading to ' + 'Python 3 (incompatible third-party plugin, ...), please open a ' + 'bug at and we\'ll ' + 'see together what we can do about it.', + DeprecationWarning) + # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2018.12.19/src/callbacks.py limnoria-2019.02.23/src/callbacks.py --- limnoria-2018.12.19/src/callbacks.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/src/callbacks.py 2019-02-21 20:54:16.000000000 +0000 @@ -939,7 +939,7 @@ return m # The '(XX more messages)' may have not the same # length in the current locale - allowedLength -= len(_('(XX more messages)')) + allowedLength -= len(_('(XX more messages)')) + 1 # bold msgs = ircutils.wrap(s, allowedLength) msgs.reverse() instant = conf.get(conf.supybot.reply.mores.instant,target) diff -Nru limnoria-2018.12.19/src/irclib.py limnoria-2019.02.23/src/irclib.py --- limnoria-2018.12.19/src/irclib.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/src/irclib.py 2019-02-21 20:54:16.000000000 +0000 @@ -978,6 +978,7 @@ self.user = ircutils.standardSubstitute(self, None, get_value('user')) self.ident = get_value('ident') self.alternateNicks = conf.supybot.nick.alternates()[:] + self.triedNicks = ircutils.IrcSet() self.password = network_config.password() self.prefix = '%s!%s@%s' % (self.nick, self.ident, 'unset.domain') # The rest. @@ -1370,20 +1371,23 @@ nick %= conf.supybot.nick() else: nick %= network_nick - return nick - else: - nick = conf.supybot.nick() - network_nick = conf.supybot.networks.get(self.network).nick() - if network_nick != '': - nick = network_nick - ret = nick - L = list(nick) - while len(L) <= 3: - L.append('`') - while ircutils.strEqual(ret, nick): - L[random.randrange(len(L))] = utils.iter.choice('0123456789') - ret = ''.join(L) - return ret + if nick not in self.triedNicks: + self.triedNicks.add(nick) + return nick + + nick = conf.supybot.nick() + network_nick = conf.supybot.networks.get(self.network).nick() + if network_nick != '': + nick = network_nick + ret = nick + L = list(nick) + while len(L) <= 3: + L.append('`') + while ret in self.triedNicks: + L[random.randrange(len(L))] = utils.iter.choice('0123456789') + ret = ''.join(L) + self.triedNicks.add(ret) + return ret def do002(self, msg): """Logs the ircd version.""" diff -Nru limnoria-2018.12.19/src/ircutils.py limnoria-2019.02.23/src/ircutils.py --- limnoria-2018.12.19/src/ircutils.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/src/ircutils.py 2019-02-21 20:54:16.000000000 +0000 @@ -181,7 +181,7 @@ elif c == '?': fd.write('.') elif c in '[{': - fd.write('[[{]') + fd.write(r'[\[{]') elif c in '}]': fd.write(r'[}\]]') elif c in '|\\': @@ -535,10 +535,25 @@ s += '\x0f' return s + def size(self): + """Returns the number of bytes needed to reproduce this context in an + IRC string.""" + prefix_size = self.bold + self.reverse + self.underline + \ + bool(self.fg) + bool(self.bg) + if self.fg and self.bg: + prefix_size += 6 # '\x03xx,yy%s' + elif self.fg or self.bg: + prefix_size += 3 # '\x03xx%s' + if prefix_size: + return prefix_size + 1 # '\x0f' + else: + return 0 + class FormatParser(object): def __init__(self, s): self.fd = minisix.io.StringIO(s) self.last = None + self.max_context_size = 0 def getChar(self): if self.last is not None: @@ -557,14 +572,22 @@ while c: if c == '\x02': context.bold = not context.bold + self.max_context_size = max( + self.max_context_size, context.size()) elif c == '\x16': context.reverse = not context.reverse + self.max_context_size = max( + self.max_context_size, context.size()) elif c == '\x1f': context.underline = not context.underline + self.max_context_size = max( + self.max_context_size, context.size()) elif c == '\x0f': context.reset() elif c == '\x03': self.getColor(context) + self.max_context_size = max( + self.max_context_size, context.size()) c = self.getChar() return context @@ -597,8 +620,17 @@ self.ungetChar(c) def wrap(s, length, break_on_hyphens = False): + # Get the maximum number of bytes needed to format a chunk of the string + # at any point. + # This is an overapproximation of what each chunk will need, but it's + # either that or make the code of byteTextWrap aware of contexts, and its + # code is complicated enough as it is already. + parser = FormatParser(s) + parser.parse() + format_overhead = parser.max_context_size + processed = [] - chunks = utils.str.byteTextWrap(s, length) + chunks = utils.str.byteTextWrap(s, length - format_overhead) context = None for chunk in chunks: if context is not None: diff -Nru limnoria-2018.12.19/src/log.py limnoria-2019.02.23/src/log.py --- limnoria-2018.12.19/src/log.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/src/log.py 2019-02-21 20:54:16.000000000 +0000 @@ -198,8 +198,17 @@ 'error was: %s' % (messagesLogFilename, utils.gen.exnToString(e))) # These are public. -formatter = Formatter('NEVER SEEN; IF YOU SEE THIS, FILE A BUG!') -pluginFormatter = PluginFormatter('NEVER SEEN; IF YOU SEE THIS, FILE A BUG!') +if sys.version_info >= (3, 8): + formatter = Formatter( + 'NEVER SEEN; IF YOU SEE THIS, FILE A BUG!', validate=False) + pluginFormatter = PluginFormatter( + 'NEVER SEEN; IF YOU SEE THIS, FILE A BUG!', validate=False) + +else: + formatter = Formatter( + 'NEVER SEEN; IF YOU SEE THIS, FILE A BUG!') + pluginFormatter = PluginFormatter( + 'NEVER SEEN; IF YOU SEE THIS, FILE A BUG!') # These are not. logging.setLoggerClass(Logger) @@ -409,7 +418,12 @@ _logger.addHandler(_handler) _logger.setLevel(-1) -_stdoutFormatter = ColorizedFormatter('IF YOU SEE THIS, FILE A BUG!') +if sys.version_info >= (3, 8): + _stdoutFormatter = ColorizedFormatter( + 'IF YOU SEE THIS, FILE A BUG!', validate=False) +else: + _stdoutFormatter = ColorizedFormatter( + 'IF YOU SEE THIS, FILE A BUG!') _stdoutHandler.setFormatter(_stdoutFormatter) _stdoutHandler.setLevel(conf.supybot.log.stdout.level()) if not conf.daemonized: diff -Nru limnoria-2018.12.19/src/utils/gen.py limnoria-2019.02.23/src/utils/gen.py --- limnoria-2018.12.19/src/utils/gen.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/src/utils/gen.py 2019-02-21 20:54:16.000000000 +0000 @@ -196,6 +196,9 @@ elif sys.version_info[0:2] >= (3, 4) and \ node.__class__ is ast.NameConstant: return True + elif sys.version_info[0:2] >= (3, 8) and \ + node.__class__ is ast.Constant: + return True else: return False if checkNode(node): diff -Nru limnoria-2018.12.19/src/utils/python.py limnoria-2019.02.23/src/utils/python.py --- limnoria-2018.12.19/src/utils/python.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/src/utils/python.py 2019-02-21 20:54:16.000000000 +0000 @@ -159,10 +159,20 @@ frame_locals = frame.f_locals for inspected in ('self', 'cls'): if inspected in frame_locals: - if hasattr(frame_locals[inspected], '__dict__') and \ - frame_locals[inspected].__dict__: - for (key, value) in frame_locals[inspected].__dict__.items(): - frame_locals['%s.%s' % (inspected, key)] = value + try: + attribute_names = dir(frame_locals[inspected]) + except Exception: # For Python 2 and Pypy + try: + attribute_names = list( + frame_locals[inspected].__dict__) + except Exception: + attribute_names = [] + for attr_name in attribute_names: + try: + v = getattr(frame_locals[inspected], attr_name) + except Exception: + v = '' + frame_locals['%s.%s' % (inspected, attr_name)] = v for key, value in frame_locals.items(): if key == '__builtins__': # This is flooding @@ -173,7 +183,7 @@ #error we don't want. try: data += repr(value) + '\n' - except: + except Exception: data += '\n' data += '\n' data += '+-----------------------+\n' diff -Nru limnoria-2018.12.19/src/utils/str.py limnoria-2019.02.23/src/utils/str.py --- limnoria-2018.12.19/src/utils/str.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/src/utils/str.py 2019-02-21 20:54:16.000000000 +0000 @@ -89,7 +89,7 @@ return s.rsplit(sep, maxsplit) def normalizeWhitespace(s, removeNewline=True): - """Normalizes the whitespace in a string; \s+ becomes one space.""" + r"""Normalizes the whitespace in a string; \s+ becomes one space.""" if not s: return str(s) # not the same reference starts_with_space = (s[0] in ' \n\t\r') @@ -525,7 +525,7 @@ def url(url): return url -_formatRe = re.compile('%((?:\d+)?\.\d+f|[bfhiLnpqrsStTuv%])') +_formatRe = re.compile(r'%((?:\d+)?\.\d+f|[bfhiLnpqrsStTuv%])') def format(s, *args, **kwargs): """w00t. diff -Nru limnoria-2018.12.19/src/version.py limnoria-2019.02.23/src/version.py --- limnoria-2018.12.19/src/version.py 2018-12-24 17:21:57.000000000 +0000 +++ limnoria-2019.02.23/src/version.py 2019-02-22 23:11:30.000000000 +0000 @@ -1,4 +1,4 @@ -version = '2018.12.19' +version = '2019.02.23' try: # For import from setup.py import supybot.utils.python supybot.utils.python._debug_software_version = version diff -Nru limnoria-2018.12.19/test/test_ircutils.py limnoria-2019.02.23/test/test_ircutils.py --- limnoria-2018.12.19/test/test_ircutils.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/test/test_ircutils.py 2019-02-21 20:54:16.000000000 +0000 @@ -227,6 +227,9 @@ r = ircutils.wrap(s, 139) self.assertTrue(max(map(pred, r)) <= 139) + s = '\x02\x16 barbazqux' + ('foobarbazqux ' * 20)[0:-1] + r = ircutils.wrap(s, 91) + self.assertTrue(max(map(pred, r)) <= 91) def testSafeArgument(self): s = 'I have been running for 9 seconds' diff -Nru limnoria-2018.12.19/test/test_utils.py limnoria-2019.02.23/test/test_utils.py --- limnoria-2018.12.19/test/test_utils.py 2018-12-24 17:21:47.000000000 +0000 +++ limnoria-2019.02.23/test/test_utils.py 2019-02-21 20:54:16.000000000 +0000 @@ -308,7 +308,7 @@ self.assertEqual(f('fooba\\rba\\z'), 'foorz') f = PRTR('s/cat/dog/i') self.assertEqual(f('CATFISH'), 'dogFISH') - f = PRTR('s/foo/foo\/bar/') + f = PRTR(r's/foo/foo\/bar/') self.assertEqual(f('foo'), 'foo/bar') f = PRTR('s/^/foo/') self.assertEqual(f('bar'), 'foobar') @@ -326,7 +326,7 @@ self.assertEqual(f('foobarbaz'), 'barbarbaz') def testPerlReToReplacerBug850931(self): - f = utils.str.perlReToReplacer('s/\b(\w+)\b/\1./g') + f = utils.str.perlReToReplacer(r's/\b(\w+)\b/\1./g') self.assertEqual(f('foo bar baz'), 'foo. bar. baz.') def testCommaAndify(self): @@ -1175,6 +1175,43 @@ s.truncate(3) self.assertEqual(s, set(['foo', 'baz', 'qux'])) +class UtilsPythonTest(SupyTestCase): + def test_dict(self): + class Foo: + def __hasattr__(self, n): + raise Exception(n) + def __getattr__(self, n): + raise Exception(n) + + def f(): + self = Foo() + self.bar = 'baz' + raise Exception('f') + + try: + f() + except: + res = utils.python.collect_extra_debug_data() + + self.assertTrue(re.search('self.bar.*=.*baz', res), res) + + def test_slots(self): + class Foo: + __slots__ = ('bar',) + def __hasattr__(self, n): + raise Exception(n) + def __getattr__(self, n): + raise Exception(n) + + def f(): + self = Foo() + self.bar = 'baz' + raise Exception('f') + + try: + f() + except: + res = utils.python.collect_extra_debug_data() # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2018.12.19/.travis.yml limnoria-2019.02.23/.travis.yml --- limnoria-2018.12.19/.travis.yml 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2019.02.23/.travis.yml 2019-02-21 20:54:16.000000000 +0000 @@ -0,0 +1,63 @@ +language: python +sudo: true +install: + - if [ "$WITH_OPT_DEPS" = "true" ] ; then pip install -vr requirements.txt; fi + - if [[ "$TRAVIS_PYTHON_VERSION" =~ ^3\.[4-9] ]] ; then pip install -v git+https://github.com/ProgVal/irctest.git; fi + - echo "y" | pip uninstall limnoria || true +# command to run tests, e.g. python setup.py test +script: + - echo $TRAVIS_PYTHON_VERSION + - python setup.py install + - supybot-test test -v --plugins-dir=./plugins/ --no-network --disable-multiprocessing --exclude=./plugins/Scheduler --exclude=./plugins/Filter + - if [[ "$TRAVIS_PYTHON_VERSION" =~ ^3\.[4-9] ]] ; then python -m irctest irctest.controllers.limnoria; fi +notifications: + email: false +matrix: + include: + - python: "2.6" + env: WITH_OPT_DEPS=false + dist: trusty + - python: "2.7" + env: WITH_OPT_DEPS=false + dist: trusty + - python: "2.7" + env: WITH_OPT_DEPS=true + dist: trusty + - python: "3.2" + env: WITH_OPT_DEPS=false + dist: trusty + - python: "3.2" + env: WITH_OPT_DEPS=true + dist: trusty + - python: "3.3" + env: WITH_OPT_DEPS=true + dist: trusty + - python: "3.4" + env: WITH_OPT_DEPS=true + dist: trusty + - python: "3.5" + env: WITH_OPT_DEPS=true + dist: trusty + - python: "3.6" + env: WITH_OPT_DEPS=true + dist: trusty + + - python: "3.7" + env: WITH_OPT_DEPS=false + dist: xenial + - python: "3.7" + env: WITH_OPT_DEPS=true + dist: xenial + - python: "3.8-dev" + env: WITH_OPT_DEPS=true + dist: xenial + - python: "nightly" + env: WITH_OPT_DEPS=true + dist: xenial + + - python: "pypy" + env: WITH_OPT_DEPS=true + dist: trusty + - python: "pypy3.3-5.2-alpha1" + env: WITH_OPT_DEPS=true + dist: trusty