Update meetbot to work under python3

Code changes to make meetbot work under python3 with limnoria.
No functional changes.

Change-Id: Ic35da2bf94b32c04651d852736cdce1d956f02e0
This commit is contained in:
Don Talton 2020-03-19 10:35:37 -07:00
parent 36a94f9d97
commit 7a1e26418b
8 changed files with 121 additions and 109 deletions

View File

@ -35,6 +35,7 @@ here. This should describe *what* the plugin does.
import supybot import supybot
import supybot.world as world import supybot.world as world
import imp
# Use this for the version of this plugin. You may wish to put a CVS keyword # Use this for the version of this plugin. You may wish to put a CVS keyword
# in here if you're keeping the plugin in CVS or some similar system. # in here if you're keeping the plugin in CVS or some similar system.
@ -50,14 +51,14 @@ __contributors__ = {}
# This is a url where the most recent plugin package can be downloaded. # This is a url where the most recent plugin package can be downloaded.
__url__ = '' # 'http://supybot.com/Members/yourname/MeetBot/download' __url__ = '' # 'http://supybot.com/Members/yourname/MeetBot/download'
import config from . import config
import plugin from . import plugin
reload(plugin) # In case we're being reloaded. imp.reload(plugin) # In case we're being reloaded.
# Add more reloads here if you add third-party modules and want them to be # Add more reloads here if you add third-party modules and want them to be
# reloaded when this plugin is reloaded. Don't forget to import them as well! # reloaded when this plugin is reloaded. Don't forget to import them as well!
if world.testing: if world.testing:
import test from . import test
Class = plugin.Class Class = plugin.Class
configure = config.configure configure = config.configure

View File

@ -37,10 +37,11 @@ import supybot.ircmsgs as ircmsgs
import time import time
import ircmeeting.meeting as meeting import ircmeeting.meeting as meeting
import supybotconfig from . import supybotconfig
import imp
# Because of the way we override names, we need to reload these in order. # Because of the way we override names, we need to reload these in order.
meeting = reload(meeting) meeting = imp.reload(meeting)
supybotconfig = reload(supybotconfig) supybotconfig = imp.reload(supybotconfig)
if supybotconfig.is_supybotconfig_enabled(meeting.Config): if supybotconfig.is_supybotconfig_enabled(meeting.Config):
supybotconfig.setup_config(meeting.Config) supybotconfig.setup_config(meeting.Config)
@ -100,13 +101,19 @@ class MeetBot(callbacks.Plugin):
irc.error("A meeting name is required, e.g., " irc.error("A meeting name is required, e.g., "
"'#startmeeting Marketing Committee'") "'#startmeeting Marketing Committee'")
return return
# This callback is used to send data to the channel: # This callback is used to send data to the channel:
def _setTopic(x): def _setTopic(x):
str(x)
irc.sendMsg(ircmsgs.topic(channel, x)) irc.sendMsg(ircmsgs.topic(channel, x))
def _sendReply(x): def _sendReply(x):
str(x)
irc.sendMsg(ircmsgs.privmsg(channel, x)) irc.sendMsg(ircmsgs.privmsg(channel, x))
def _channelNicks(): def _channelNicks():
return irc.state.channels[channel].users return irc.state.channels[channel].users
M = meeting.Meeting(channel=channel, owner=nick, M = meeting.Meeting(channel=channel, owner=nick,
oldtopic=irc.state.channels[channel].topic, oldtopic=irc.state.channels[channel].topic,
writeRawLog=True, writeRawLog=True,
@ -142,14 +149,14 @@ class MeetBot(callbacks.Plugin):
nick = irc.nick nick = irc.nick
channel = msg.args[0] channel = msg.args[0]
payload = msg.args[1] payload = msg.args[1]
Mkey = (channel,irc.network) Mkey = (channel, irc.network)
M = meeting_cache.get(Mkey, None) M = meeting_cache.get(Mkey, None)
if M is not None: if M is not None:
M.addrawline(nick, payload) M.addrawline(nick, payload)
except: except:
import traceback import traceback
print traceback.print_exc() print(traceback.print_exc())
print "(above exception in outFilter, ignoring)" print("(above exception in outFilter, ignoring)")
return msg return msg
# These are admin commands, for use by the bot owner when there # These are admin commands, for use by the bot owner when there
@ -170,7 +177,7 @@ class MeetBot(callbacks.Plugin):
Save all currently active meetings.""" Save all currently active meetings."""
numSaved = 0 numSaved = 0
for M in meeting_cache.iteritems(): for M in meeting_cache.items():
M.config.save() M.config.save()
irc.reply("Saved %d meetings."%numSaved) irc.reply("Saved %d meetings."%numSaved)
savemeetings = wrap(savemeetings, ['admin']) savemeetings = wrap(savemeetings, ['admin'])
@ -261,46 +268,46 @@ class MeetBot(callbacks.Plugin):
pingall = wrap(pingall, [optional('text', None)]) pingall = wrap(pingall, [optional('text', None)])
def __getattr__(self, name): # def __getattr__(self, name):
"""Proxy between proper supybot commands and # MeetBot commands. # """Proxy between proper supybot commands and # MeetBot commands.
#
This allows you to use MeetBot: <command> <line of the command> # This allows you to use MeetBot: <command> <line of the command>
instead of the typical #command version. However, it's disabled # instead of the typical #command version. However, it's disabled
by default as there are some possible unresolved issues with it. # by default as there are some possible unresolved issues with it.
#
To enable this, you must comment out a line in the main code. # To enable this, you must comment out a line in the main code.
It may be enabled in a future version. # It may be enabled in a future version.
""" # """
# First, proxy to our parent classes (__parent__ set in __init__) # # First, proxy to our parent classes (__parent__ set in __init__)
try: # try:
return self.__parent.__getattr__(name) # return self.__parent.__getattr__(name)
except AttributeError: # except AttributeError:
pass # pass
# Disabled for now. Uncomment this if you want to use this. # # Disabled for now. Uncomment this if you want to use this.
raise AttributeError # raise AttributeError
#
if not hasattr(meeting.Meeting, "do_"+name): # if not hasattr(meeting.Meeting, "do_"+name):
raise AttributeError # raise AttributeError
#
def wrapped_function(self, irc, msg, args, message): # def wrapped_function(self, irc, msg, args, message):
channel = msg.args[0] # channel = msg.args[0]
payload = msg.args[1] # payload = msg.args[1]
#
#from fitz import interactnow ; reload(interactnow) # #from fitz import interactnow ; reload(interactnow)
#
#print type(payload) # #print type(payload)
payload = "#%s %s"%(name,message) # payload = "#%s %s"%(name,message)
#print payload # #print payload
import copy # import copy
msg = copy.copy(msg) # msg = copy.copy(msg)
msg.args = (channel, payload) # msg.args = (channel, payload)
#
self.doPrivmsg(irc, msg) # self.doPrivmsg(irc, msg)
# Give it the signature we need to be a callable supybot # # Give it the signature we need to be a callable supybot
# command (it does check more than I'd like). Heavy Wizardry. # # command (it does check more than I'd like). Heavy Wizardry.
instancemethod = type(self.__getattr__) # instancemethod = type(self.__getattr__)
wrapped_function = wrap(wrapped_function, [optional('text', '')]) # wrapped_function = wrap(wrapped_function, [optional('text', '')])
return instancemethod(wrapped_function, self, MeetBot) # return instancemethod(wrapped_function, self, MeetBot)
Class = MeetBot Class = MeetBot

View File

@ -60,14 +60,14 @@ class WriterMap(registry.String):
writer_map[ext] = getattr(writers, writer) writer_map[ext] = getattr(writers, writer)
self.setValue(writer_map) self.setValue(writer_map)
def setValue(self, writer_map): def setValue(self, writer_map):
for e, w in writer_map.iteritems(): for e, w in writer_map.items():
if not hasattr(w, "format"): if not hasattr(w, "format"):
raise ValueError("Writer %s must have method .format()"% raise ValueError("Writer %s must have method .format()"%
w.__name__) w.__name__)
self.value = writer_map self.value = writer_map
def __str__(self): def __str__(self):
writers_string = [ ] writers_string = [ ]
for ext, w in self.value.iteritems(): for ext, w in self.value.items():
name = w.__name__ name = w.__name__
writers_string.append("%s:%s"%(name, ext)) writers_string.append("%s:%s"%(name, ext))
return " ".join(writers_string) return " ".join(writers_string)
@ -81,7 +81,7 @@ class SupybotConfigProxy(object):
# We need to call the __init__ *after* we have rebound the # We need to call the __init__ *after* we have rebound the
# method to get variables from the config proxy. # method to get variables from the config proxy.
old_init = self.__C.__init__ old_init = self.__C.__init__
new_init = types.MethodType(old_init.im_func, self, old_init.im_class) new_init = types.MethodType(old_init.__func__, self, old_init.__self__.__class__)
new_init(*args, **kwargs) new_init(*args, **kwargs)
def __getattr__(self, attrname): def __getattr__(self, attrname):
@ -91,7 +91,7 @@ class SupybotConfigProxy(object):
if attrname in settable_attributes: if attrname in settable_attributes:
M = self.M M = self.M
value = M._registryValue(attrname, channel=M.channel) value = M._registryValue(attrname, channel=M.channel)
if not isinstance(value, (str, unicode)): if not isinstance(value, str):
return value return value
# '.' is used to mean "this is not set, use the default # '.' is used to mean "this is not set, use the default
# value from the python config class. # value from the python config class.
@ -122,7 +122,7 @@ class SupybotConfigProxy(object):
# values). This will slow things down a little bit, but # values). This will slow things down a little bit, but
# that's just the cost of duing business. # that's just the cost of duing business.
if hasattr(value, 'im_func'): if hasattr(value, 'im_func'):
return types.MethodType(value.im_func, self, value.im_class) return types.MethodType(value.__func__, self, value.__self__.__class__)
return value return value
@ -145,7 +145,7 @@ def setup_config(OriginalConfig):
continue continue
attr = getattr(OriginalConfig, attrname) attr = getattr(OriginalConfig, attrname)
# Don't configure attributes that aren't strings. # Don't configure attributes that aren't strings.
if isinstance(attr, (str, unicode)): if isinstance(attr, str):
attr = attr.replace('\n', '\\n') attr = attr.replace('\n', '\\n')
# For a global value: conf.registerGlobalValue and remove the # For a global value: conf.registerGlobalValue and remove the
# channel= option from registryValue call above. # channel= option from registryValue call above.

View File

@ -69,12 +69,12 @@ class MeetBotTestCase(ChannelPluginTestCase):
groups = re.search(test[0], line).groups() groups = re.search(test[0], line).groups()
# Output pattern depends on input pattern # Output pattern depends on input pattern
if isinstance(test[1], int): if isinstance(test[1], int):
print groups[test[1]-1], reply print(groups[test[1]-1], reply)
assert re.search(re.escape(groups[test[1]-1]), reply),\ assert re.search(re.escape(groups[test[1]-1]), reply),\
'line "%s" gives output "%s"'%(line, reply) 'line "%s" gives output "%s"'%(line, reply)
# Just match the given pattern. # Just match the given pattern.
else: else:
print test[1], reply print(test[1], reply)
assert re.search(test[1], reply.decode('utf-8')), \ assert re.search(test[1], reply.decode('utf-8')), \
'line "%s" gives output "%s"'%(line, reply) 'line "%s" gives output "%s"'%(line, reply)

View File

@ -33,7 +33,7 @@ import os
import re import re
import time import time
import writers from . import writers
#from writers import html, rst #from writers import html, rst
import itertools import itertools
@ -84,7 +84,7 @@ class _BaseItem(object):
return replacements return replacements
def template(self, M, escapewith): def template(self, M, escapewith):
template = { } template = { }
for k,v in self.get_replacements(M, escapewith).iteritems(): for k,v in self.get_replacements(M, escapewith).items():
if k not in ('itemtype', 'line', 'topic', if k not in ('itemtype', 'line', 'topic',
'url', 'url_quoteescaped', 'url', 'url_quoteescaped',
'nick', 'time', 'link', 'anchor'): 'nick', 'time', 'link', 'anchor'):

View File

@ -35,10 +35,11 @@ import re
import stat import stat
import textwrap import textwrap
import writers from . import writers
import items from . import items
reload(writers) import imp
reload(items) imp.reload(writers)
imp.reload(items)
__version__ = "0.1.4" __version__ = "0.1.4"
@ -104,10 +105,10 @@ class Config(object):
input_codec = 'utf-8' input_codec = 'utf-8'
output_codec = 'utf-8' output_codec = 'utf-8'
# Functions to do the i/o conversion. # Functions to do the i/o conversion.
def enc(self, text): # def enc(self, text):
return text.encode(self.output_codec, 'replace') # return text.encode(self.output_codec, 'replace')
def dec(self, text): # def dec(self, text):
return text.decode(self.input_codec, 'replace') # return text.decode(self.input_codec, 'replace')
# Write out select logfiles # Write out select logfiles
update_realtime = True update_realtime = True
# CSS configs: # CSS configs:
@ -132,14 +133,14 @@ class Config(object):
self.M = M self.M = M
self.writers = { } self.writers = { }
# Update config values with anything we may have # Update config values with anything we may have
for k,v in extraConfig.iteritems(): for k,v in extraConfig.items():
setattr(self, k, v) setattr(self, k, v)
if hasattr(self, "init_hook"): if hasattr(self, "init_hook"):
self.init_hook() self.init_hook()
if writeRawLog: if writeRawLog:
self.writers['.log.txt'] = writers.TextLog(self.M) self.writers['.log.txt'] = writers.TextLog(self.M)
for extension, writer in self.writer_map.iteritems(): for extension, writer in self.writer_map.items():
self.writers[extension] = writer(self.M) self.writers[extension] = writer(self.M)
self.safeMode = safeMode self.safeMode = safeMode
def filename(self, url=False): def filename(self, url=False):
@ -219,9 +220,9 @@ class Config(object):
# If it doesn't, then it's assumed that the write took # If it doesn't, then it's assumed that the write took
# care of writing (or publishing or emailing or wikifying) # care of writing (or publishing or emailing or wikifying)
# it itself. # it itself.
if isinstance(text, unicode): # if isinstance(text, str):
text = self.enc(text) # text = self.enc(text)
if isinstance(text, (str, unicode)): if isinstance(text, str):
# Have a way to override saving, so no disk files are written. # Have a way to override saving, so no disk files are written.
if getattr(self, "dontSave", False): if getattr(self, "dontSave", False):
pass pass
@ -281,7 +282,7 @@ else:
# First source of config: try just plain importing it # First source of config: try just plain importing it
try: try:
import meetingLocalConfig import meetingLocalConfig
meetingLocalConfig = reload(meetingLocalConfig) meetingLocalConfig = imp.reload(meetingLocalConfig)
if hasattr(meetingLocalConfig, 'Config'): if hasattr(meetingLocalConfig, 'Config'):
LocalConfig = meetingLocalConfig.Config LocalConfig = meetingLocalConfig.Config
except ImportError: except ImportError:
@ -291,7 +292,7 @@ else:
fname = os.path.join(dirname, "meetingLocalConfig.py") fname = os.path.join(dirname, "meetingLocalConfig.py")
if os.access(fname, os.F_OK): if os.access(fname, os.F_OK):
meetingLocalConfig = { } meetingLocalConfig = { }
execfile(fname, meetingLocalConfig) exec(compile(open(fname, "rb").read(), fname, 'exec'), meetingLocalConfig)
LocalConfig = meetingLocalConfig["Config"] LocalConfig = meetingLocalConfig["Config"]
break break
if LocalConfig is not None: if LocalConfig is not None:
@ -491,9 +492,9 @@ class MeetingCommands(object):
m = 'Voted on "%s?" Results are' % self._voteTopic m = 'Voted on "%s?" Results are' % self._voteTopic
self.reply(m) self.reply(m)
self.do_showvote(**kwargs) self.do_showvote(**kwargs)
for k,s in self._votes.iteritems(): for k,s in self._votes.items():
vote = self._voteOptions[map(unicode.lower, vote = self._voteOptions[list(map(str.lower,
self._voteOptions).index(k)] self._voteOptions)).index(k)]
m += ", %s: %s" % (vote, len(s)) m += ", %s: %s" % (vote, len(s))
m = items.Vote(nick=nick, line=m, **kwargs) m = items.Vote(nick=nick, line=m, **kwargs)
self.additem(m) self.additem(m)
@ -505,7 +506,7 @@ class MeetingCommands(object):
"""Vote for specific voting topic option.""" """Vote for specific voting topic option."""
if self._voteTopic is None: return if self._voteTopic is None: return
vote = line.lower() vote = line.lower()
if vote in map(unicode.lower, self._voteOptions): if vote in list(map(str.lower, self._voteOptions)):
oldvote = self._voters.get(nick) oldvote = self._voters.get(nick)
if oldvote is not None: if oldvote is not None:
self._votes[oldvote].remove(nick) self._votes[oldvote].remove(nick)
@ -520,13 +521,13 @@ class MeetingCommands(object):
def do_showvote(self, **kwargs): def do_showvote(self, **kwargs):
"""Show intermediate vote results.""" """Show intermediate vote results."""
if self._voteTopic is None: return if self._voteTopic is None: return
for k, s in self._votes.iteritems(): for k, s in self._votes.items():
# Attempt to print all the names while obeying the 512 character # Attempt to print all the names while obeying the 512 character
# limit. Would probably be better to calculate message overhead and # limit. Would probably be better to calculate message overhead and
# determine wraps()s width argument based on that. # determine wraps()s width argument based on that.
ms = textwrap.wrap(", ".join(s), 400) ms = textwrap.wrap(", ".join(s), 400)
vote = self._voteOptions[map(unicode.lower, vote = self._voteOptions[list(map(str.lower,
self._voteOptions).index(k)] self._voteOptions)).index(k)]
for m2 in ms: for m2 in ms:
m1 = "%s (%s): " % (vote, len(s)) m1 = "%s (%s): " % (vote, len(s))
self.reply(m1 + m2) self.reply(m1 + m2)
@ -559,7 +560,7 @@ class Meeting(MeetingCommands, object):
self.config = Config(self, writeRawLog=writeRawLog, safeMode=safeMode, self.config = Config(self, writeRawLog=writeRawLog, safeMode=safeMode,
extraConfig=extraConfig) extraConfig=extraConfig)
if oldtopic: if oldtopic:
self.oldtopic = self.config.dec(oldtopic) self.oldtopic = oldtopic
else: else:
self.oldtopic = None self.oldtopic = None
self.lines = [ ] self.lines = [ ]
@ -582,15 +583,15 @@ class Meeting(MeetingCommands, object):
def reply(self, x): def reply(self, x):
"""Send a reply to the IRC channel.""" """Send a reply to the IRC channel."""
if hasattr(self, '_sendReply') and not self._lurk: if hasattr(self, '_sendReply') and not self._lurk:
self._sendReply(self.config.enc(x)) self._sendReply(x)
else: else:
print "REPLY:", self.config.enc(x) print("REPLY:", x)
def topic(self, x): def topic(self, x):
"""Set the topic in the IRC channel.""" """Set the topic in the IRC channel."""
if hasattr(self, '_setTopic') and not self._lurk: if hasattr(self, '_setTopic') and not self._lurk:
self._setTopic(self.config.enc(x)) self._setTopic(x)
else: else:
print "TOPIC:", self.config.enc(x) print("TOPIC:", x)
def settopic(self): def settopic(self):
"The actual code to set the topic" "The actual code to set the topic"
if self._meetingTopic: if self._meetingTopic:
@ -614,8 +615,8 @@ class Meeting(MeetingCommands, object):
linenum = self.addrawline(nick, line, time_) linenum = self.addrawline(nick, line, time_)
if time_ is None: time_ = time.localtime() if time_ is None: time_ = time.localtime()
nick = self.config.dec(nick) # nick = self.config.dec(nick)
line = self.config.dec(line) # line = self.config.dec(line)
# Handle any commands given in the line. # Handle any commands given in the line.
matchobj = self.config.command_RE.match(line) matchobj = self.config.command_RE.match(line)
@ -636,8 +637,8 @@ class Meeting(MeetingCommands, object):
def addrawline(self, nick, line, time_=None): def addrawline(self, nick, line, time_=None):
"""This adds a line to the log, bypassing command execution. """This adds a line to the log, bypassing command execution.
""" """
nick = self.config.dec(nick) # nick = self.config.dec(nick)
line = self.config.dec(line) # line = self.config.dec(line)
self.addnick(nick) self.addnick(nick)
line = line.strip(' \x01') # \x01 is present in ACTIONs line = line.strip(' \x01') # \x01 is present in ACTIONs
# Setting a custom time is useful when replying logs, # Setting a custom time is useful when replying logs,
@ -736,7 +737,7 @@ if __name__ == '__main__':
filename = m.group(1) filename = m.group(1)
else: else:
filename = os.path.splitext(fname)[0] filename = os.path.splitext(fname)[0]
print 'Saving to:', filename print('Saving to:', filename)
channel = '#'+os.path.basename(sys.argv[2]).split('.')[0] channel = '#'+os.path.basename(sys.argv[2]).split('.')[0]
M = Meeting(channel=channel, owner=None, M = Meeting(channel=channel, owner=None,
@ -760,5 +761,5 @@ if __name__ == '__main__':
M.addline(nick, "ACTION "+line, time_=time_) M.addline(nick, "ACTION "+line, time_=time_)
#M.save() # should be done by #endmeeting in the logs! #M.save() # should be done by #endmeeting in the logs!
else: else:
print 'Command "%s" not found.'%sys.argv[1] print('Command "%s" not found.'%sys.argv[1])

View File

@ -38,7 +38,7 @@ import time
# Needed for testing with isinstance() for properly writing. # Needed for testing with isinstance() for properly writing.
#from items import Topic, Action #from items import Topic, Action
import items from . import items
# Data sanitizing for various output methods # Data sanitizing for various output methods
def html(text): def html(text):
@ -73,7 +73,7 @@ def makeNickRE(nick):
return re.compile('\\b'+re.escape(nick)+'\\b', re.IGNORECASE) return re.compile('\\b'+re.escape(nick)+'\\b', re.IGNORECASE)
def MeetBotVersion(): def MeetBotVersion():
import meeting from . import meeting
if hasattr(meeting, '__version__'): if hasattr(meeting, '__version__'):
return ' '+meeting.__version__ return ' '+meeting.__version__
else: else:
@ -121,12 +121,12 @@ class _BaseWriter(object):
'MeetBotVersion':MeetBotVersion(), 'MeetBotVersion':MeetBotVersion(),
} }
def iterNickCounts(self): def iterNickCounts(self):
nicks = [ (n,c) for (n,c) in self.M.attendees.iteritems() ] nicks = [ (n,c) for (n,c) in self.M.attendees.items() ]
nicks.sort(key=lambda x: x[1], reverse=True) nicks.sort(key=lambda x: x[1], reverse=True)
return nicks return nicks
def iterActionItemsNick(self): def iterActionItemsNick(self):
for nick in sorted(self.M.attendees.keys(), key=lambda x: x.lower()): for nick in sorted(list(self.M.attendees.keys()), key=lambda x: x.lower()):
nick_re = makeNickRE(nick) nick_re = makeNickRE(nick)
def nickitems(nick_re): def nickitems(nick_re):
for m in self.M.minutes: for m in self.M.minutes:
@ -323,23 +323,23 @@ class _CSSmanager(object):
# Stylesheet specified # Stylesheet specified
if getattr(self.M.config, 'cssEmbed_'+name, True): if getattr(self.M.config, 'cssEmbed_'+name, True):
# external stylesheet # external stylesheet
css = file(css_fname).read() css = open(css_fname, 'r').read()
return self._css_head%css return self._css_head%css
else: else:
# linked stylesheet # linked stylesheet
css_head = ('''<link rel="stylesheet" type="text/css" ''' css_head = ('''<link rel="stylesheet" type="text/css" '''
'''href="%s">'''%cssfile) '''href="%s">'''%cssfile)
return css_head return css_head
except Exception, exc: except Exception as exc:
if not self.M.config.safeMode: if not self.M.config.safeMode:
raise raise
import traceback import traceback
traceback.print_exc() traceback.print_exc()
print "(exception above ignored, continuing)" print("(exception above ignored, continuing)")
try: try:
css_fname = os.path.join(os.path.dirname(__file__), css_fname = os.path.join(os.path.dirname(__file__),
'css-'+name+'-default.css') 'css-'+name+'-default.css')
css = open(css_fname).read() css = open(css_fname, 'r').read()
return self._css_head%css return self._css_head%css
except: except:
if not self.M.config.safeMode: if not self.M.config.safeMode:
@ -471,9 +471,9 @@ class HTMLlog2(_BaseWriter, _CSSmanager):
'nick':html(m.group('nick')), 'nick':html(m.group('nick')),
'line':html(m.group('line')),}) 'line':html(m.group('line')),})
continue continue
print l print(l)
print m.groups() print(m.groups())
print "**error**", l print("**error**", l)
css = self.getCSS(name='log') css = self.getCSS(name='log')
return html_template%{'pageTitle':"%s log"%html(M.channel), return html_template%{'pageTitle':"%s log"%html(M.channel),
@ -850,7 +850,7 @@ class ReST(_BaseWriter):
# Action Items, by person (This could be made lots more efficient) # Action Items, by person (This could be made lots more efficient)
ActionItemsPerson = [ ] ActionItemsPerson = [ ]
for nick in sorted(M.attendees.keys(), key=lambda x: x.lower()): for nick in sorted(list(M.attendees.keys()), key=lambda x: x.lower()):
nick_re = makeNickRE(nick) nick_re = makeNickRE(nick)
headerPrinted = False headerPrinted = False
for m in M.minutes: for m in M.minutes:
@ -956,7 +956,7 @@ class Text(_BaseWriter):
ActionItemsPerson = [ ] ActionItemsPerson = [ ]
ActionItemsPerson.append(self.heading('Action items, by person')) ActionItemsPerson.append(self.heading('Action items, by person'))
numberAssigned = 0 numberAssigned = 0
for nick in sorted(M.attendees.keys(), key=lambda x: x.lower()): for nick in sorted(list(M.attendees.keys()), key=lambda x: x.lower()):
nick_re = makeNickRE(nick) nick_re = makeNickRE(nick)
headerPrinted = False headerPrinted = False
for m in M.minutes: for m in M.minutes:
@ -1084,7 +1084,7 @@ class MediaWiki(_BaseWriter):
ActionItemsPerson = [ ] ActionItemsPerson = [ ]
ActionItemsPerson.append(self.heading('Action items, by person')) ActionItemsPerson.append(self.heading('Action items, by person'))
numberAssigned = 0 numberAssigned = 0
for nick in sorted(M.attendees.keys(), key=lambda x: x.lower()): for nick in sorted(list(M.attendees.keys()), key=lambda x: x.lower()):
nick_re = makeNickRE(nick) nick_re = makeNickRE(nick)
headerPrinted = False headerPrinted = False
for m in M.minutes: for m in M.minutes:
@ -1190,7 +1190,7 @@ class PmWiki(MediaWiki, object):
return '%s %s\n'%('!'*(level+1), name) return '%s %s\n'%('!'*(level+1), name)
def replacements(self): def replacements(self):
#repl = super(PmWiki, self).replacements(self) # fails, type checking #repl = super(PmWiki, self).replacements(self) # fails, type checking
repl = MediaWiki.replacements.im_func(self) repl = MediaWiki.replacements.__func__(self)
repl['pageTitleHeading'] = self.heading(repl['pageTitle'],level=0) repl['pageTitleHeading'] = self.heading(repl['pageTitle'],level=0)
return repl return repl

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
limnoria==2020.1.31
pkg-resources==0.0.0
Pygments==2.6.1