diff --git a/MeetBot/__init__.py b/MeetBot/__init__.py index 5a43105..fe7332f 100644 --- a/MeetBot/__init__.py +++ b/MeetBot/__init__.py @@ -35,6 +35,8 @@ here. This should describe *what* the plugin does. import supybot import supybot.world as world +import sys +import importlib # 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. @@ -50,14 +52,21 @@ __contributors__ = {} # This is a url where the most recent plugin package can be downloaded. __url__ = '' # 'http://supybot.com/Members/yourname/MeetBot/download' -import config -import plugin -reload(plugin) # In case we're being reloaded. +from . import config +from . import plugin +if sys.version_info >= (3, 4): + from importlib import reload +else: + from imp import reload + + # In case we're being reloaded. +importlib.reload(config) +importlib.reload(plugin) # 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! if world.testing: - import test + from . import test Class = plugin.Class configure = config.configure diff --git a/MeetBot/plugin.py b/MeetBot/plugin.py index 217cff5..80751ef 100644 --- a/MeetBot/plugin.py +++ b/MeetBot/plugin.py @@ -37,10 +37,11 @@ import supybot.ircmsgs as ircmsgs import time import ircmeeting.meeting as meeting -import supybotconfig +from . import supybotconfig +import importlib # Because of the way we override names, we need to reload these in order. -meeting = reload(meeting) -supybotconfig = reload(supybotconfig) +meeting = importlib.reload(meeting) +supybotconfig = importlib.reload(supybotconfig) if supybotconfig.is_supybotconfig_enabled(meeting.Config): supybotconfig.setup_config(meeting.Config) @@ -148,8 +149,8 @@ class MeetBot(callbacks.Plugin): M.addrawline(nick, payload) except: import traceback - print traceback.print_exc() - print "(above exception in outFilter, ignoring)" + print(traceback.print_exc()) + print("(above exception in outFilter, ignoring)") return msg # These are admin commands, for use by the bot owner when there @@ -170,7 +171,7 @@ class MeetBot(callbacks.Plugin): Save all currently active meetings.""" numSaved = 0 - for M in meeting_cache.iteritems(): + for M in meeting_cache.items(): M.config.save() irc.reply("Saved %d meetings."%numSaved) savemeetings = wrap(savemeetings, ['admin']) diff --git a/MeetBot/supybotconfig.py b/MeetBot/supybotconfig.py index d4921a9..36c65f5 100644 --- a/MeetBot/supybotconfig.py +++ b/MeetBot/supybotconfig.py @@ -60,14 +60,14 @@ class WriterMap(registry.String): writer_map[ext] = getattr(writers, writer) self.setValue(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"): raise ValueError("Writer %s must have method .format()"% w.__name__) self.value = writer_map def __str__(self): writers_string = [ ] - for ext, w in self.value.iteritems(): + for ext, w in self.value.items(): name = w.__name__ writers_string.append("%s:%s"%(name, ext)) return " ".join(writers_string) @@ -81,7 +81,7 @@ class SupybotConfigProxy(object): # We need to call the __init__ *after* we have rebound the # method to get variables from the config proxy. 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) def __getattr__(self, attrname): @@ -91,7 +91,7 @@ class SupybotConfigProxy(object): if attrname in settable_attributes: M = self.M value = M._registryValue(attrname, channel=M.channel) - if not isinstance(value, (str, unicode)): + if not isinstance(value, str): return value # '.' is used to mean "this is not set, use the default # value from the python config class. @@ -122,7 +122,7 @@ class SupybotConfigProxy(object): # values). This will slow things down a little bit, but # that's just the cost of duing business. 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 @@ -145,7 +145,7 @@ def setup_config(OriginalConfig): continue attr = getattr(OriginalConfig, attrname) # Don't configure attributes that aren't strings. - if isinstance(attr, (str, unicode)): + if isinstance(attr, str): attr = attr.replace('\n', '\\n') # For a global value: conf.registerGlobalValue and remove the # channel= option from registryValue call above. diff --git a/MeetBot/test.py b/MeetBot/test.py index 28dba8a..b29a4dd 100644 --- a/MeetBot/test.py +++ b/MeetBot/test.py @@ -69,12 +69,12 @@ class MeetBotTestCase(ChannelPluginTestCase): groups = re.search(test[0], line).groups() # Output pattern depends on input pattern 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),\ 'line "%s" gives output "%s"'%(line, reply) # Just match the given pattern. else: - print test[1], reply + print(test[1], reply) assert re.search(test[1], reply.decode('utf-8')), \ 'line "%s" gives output "%s"'%(line, reply) diff --git a/ircmeeting/items.py b/ircmeeting/items.py index 6b04964..29da06b 100644 --- a/ircmeeting/items.py +++ b/ircmeeting/items.py @@ -33,7 +33,7 @@ import os import re import time -import writers +from . import writers #from writers import html, rst import itertools @@ -84,7 +84,7 @@ class _BaseItem(object): return replacements def template(self, M, escapewith): 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', 'url', 'url_quoteescaped', 'nick', 'time', 'link', 'anchor'): diff --git a/ircmeeting/meeting.py b/ircmeeting/meeting.py index 6b9d2ff..6990735 100644 --- a/ircmeeting/meeting.py +++ b/ircmeeting/meeting.py @@ -35,10 +35,11 @@ import re import stat import textwrap -import writers -import items -reload(writers) -reload(items) +from . import writers +from . import items +import importlib +importlib.reload(writers) +importlib.reload(items) __version__ = "0.1.4" @@ -132,14 +133,14 @@ class Config(object): self.M = M self.writers = { } # Update config values with anything we may have - for k,v in extraConfig.iteritems(): + for k,v in extraConfig.items(): setattr(self, k, v) if hasattr(self, "init_hook"): self.init_hook() if writeRawLog: 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.safeMode = safeMode def filename(self, url=False): @@ -219,9 +220,9 @@ class Config(object): # If it doesn't, then it's assumed that the write took # care of writing (or publishing or emailing or wikifying) # it itself. - if isinstance(text, unicode): + if isinstance(text, str): 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. if getattr(self, "dontSave", False): pass @@ -281,7 +282,7 @@ else: # First source of config: try just plain importing it try: import meetingLocalConfig - meetingLocalConfig = reload(meetingLocalConfig) + meetingLocalConfig = importlib.reload(meetingLocalConfig) if hasattr(meetingLocalConfig, 'Config'): LocalConfig = meetingLocalConfig.Config except ImportError: @@ -291,7 +292,7 @@ else: fname = os.path.join(dirname, "meetingLocalConfig.py") if os.access(fname, os.F_OK): meetingLocalConfig = { } - execfile(fname, meetingLocalConfig) + exec(compile(open(fname, "rb").read(), fname, 'exec'), meetingLocalConfig) LocalConfig = meetingLocalConfig["Config"] break if LocalConfig is not None: @@ -491,9 +492,9 @@ class MeetingCommands(object): m = 'Voted on "%s?" Results are' % self._voteTopic self.reply(m) self.do_showvote(**kwargs) - for k,s in self._votes.iteritems(): - vote = self._voteOptions[map(unicode.lower, - self._voteOptions).index(k)] + for k,s in self._votes.items(): + vote = self._voteOptions[list(map(str.lower, + self._voteOptions)).index(k)] m += ", %s: %s" % (vote, len(s)) m = items.Vote(nick=nick, line=m, **kwargs) self.additem(m) @@ -505,7 +506,7 @@ class MeetingCommands(object): """Vote for specific voting topic option.""" if self._voteTopic is None: return 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) if oldvote is not None: self._votes[oldvote].remove(nick) @@ -520,13 +521,13 @@ class MeetingCommands(object): def do_showvote(self, **kwargs): """Show intermediate vote results.""" 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 # limit. Would probably be better to calculate message overhead and # determine wraps()s width argument based on that. ms = textwrap.wrap(", ".join(s), 400) - vote = self._voteOptions[map(unicode.lower, - self._voteOptions).index(k)] + vote = self._voteOptions[list(map(str.lower, + self._voteOptions)).index(k)] for m2 in ms: m1 = "%s (%s): " % (vote, len(s)) self.reply(m1 + m2) @@ -584,13 +585,13 @@ class Meeting(MeetingCommands, object): if hasattr(self, '_sendReply') and not self._lurk: self._sendReply(self.config.enc(x)) else: - print "REPLY:", self.config.enc(x) + print("REPLY:", self.config.enc(x)) def topic(self, x): """Set the topic in the IRC channel.""" if hasattr(self, '_setTopic') and not self._lurk: self._setTopic(self.config.enc(x)) else: - print "TOPIC:", self.config.enc(x) + print("TOPIC:", self.config.enc(x)) def settopic(self): "The actual code to set the topic" if self._meetingTopic: @@ -736,7 +737,7 @@ if __name__ == '__main__': filename = m.group(1) else: filename = os.path.splitext(fname)[0] - print 'Saving to:', filename + print('Saving to:', filename) channel = '#'+os.path.basename(sys.argv[2]).split('.')[0] M = Meeting(channel=channel, owner=None, @@ -760,5 +761,5 @@ if __name__ == '__main__': M.addline(nick, "ACTION "+line, time_=time_) #M.save() # should be done by #endmeeting in the logs! else: - print 'Command "%s" not found.'%sys.argv[1] + print('Command "%s" not found.'%sys.argv[1]) diff --git a/ircmeeting/writers.py b/ircmeeting/writers.py index f214ea5..e33520f 100644 --- a/ircmeeting/writers.py +++ b/ircmeeting/writers.py @@ -38,7 +38,7 @@ import time # Needed for testing with isinstance() for properly writing. #from items import Topic, Action -import items +from . import items # Data sanitizing for various output methods def html(text): @@ -73,7 +73,7 @@ def makeNickRE(nick): return re.compile('\\b'+re.escape(nick)+'\\b', re.IGNORECASE) def MeetBotVersion(): - import meeting + from . import meeting if hasattr(meeting, '__version__'): return ' '+meeting.__version__ else: @@ -121,12 +121,12 @@ class _BaseWriter(object): 'MeetBotVersion':MeetBotVersion(), } 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) return nicks 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) def nickitems(nick_re): for m in self.M.minutes: @@ -330,12 +330,12 @@ class _CSSmanager(object): css_head = (''''''%cssfile) return css_head - except Exception, exc: + except Exception as exc: if not self.M.config.safeMode: raise import traceback traceback.print_exc() - print "(exception above ignored, continuing)" + print("(exception above ignored, continuing)") try: css_fname = os.path.join(os.path.dirname(__file__), 'css-'+name+'-default.css') @@ -471,9 +471,9 @@ class HTMLlog2(_BaseWriter, _CSSmanager): 'nick':html(m.group('nick')), 'line':html(m.group('line')),}) continue - print l - print m.groups() - print "**error**", l + print(l) + print(m.groups()) + print("**error**", l) css = self.getCSS(name='log') 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) 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) headerPrinted = False for m in M.minutes: @@ -956,7 +956,7 @@ class Text(_BaseWriter): ActionItemsPerson = [ ] ActionItemsPerson.append(self.heading('Action items, by person')) 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) headerPrinted = False for m in M.minutes: @@ -1084,7 +1084,7 @@ class MediaWiki(_BaseWriter): ActionItemsPerson = [ ] ActionItemsPerson.append(self.heading('Action items, by person')) 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) headerPrinted = False for m in M.minutes: @@ -1190,7 +1190,7 @@ class PmWiki(MediaWiki, object): return '%s %s\n'%('!'*(level+1), name) def replacements(self): #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) return repl