You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by he...@apache.org on 2015/05/29 22:41:00 UTC

[38/45] allura git commit: [#7878] Used 2to3 to see what issues would come up

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/forum_main.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/forum_main.py b/ForgeDiscussion/forgediscussion/forum_main.py
index 542b3f0..d4bb6e1 100644
--- a/ForgeDiscussion/forgediscussion/forum_main.py
+++ b/ForgeDiscussion/forgediscussion/forum_main.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -17,7 +21,7 @@
 
 #-*- python -*-
 import logging
-import urllib
+import urllib.request, urllib.parse, urllib.error
 import json
 
 # Non-stdlib imports
@@ -41,7 +45,7 @@ from forgediscussion import utils
 from forgediscussion import version
 from .controllers import RootController, RootRestController
 
-from widgets.admin import OptionsAdmin, AddForum
+from .widgets.admin import OptionsAdmin, AddForum
 
 
 log = logging.getLogger(__name__)
@@ -100,7 +104,7 @@ class ForgeDiscussionApp(Application):
         log.info('Message from %s (%s)',
                  topic, self.config.options.mount_point)
         log.info('Headers are: %s', message['headers'])
-        shortname = urllib.unquote_plus(topic.replace('.', '/'))
+        shortname = urllib.parse.unquote_plus(topic.replace('.', '/'))
         forum = DM.Forum.query.get(
             shortname=shortname, app_config_id=self.config._id)
         if forum is None:
@@ -158,14 +162,9 @@ class ForgeDiscussionApp(Application):
             for f in forums:
                 if has_access(f, 'read')():
                     if f.url() in request.url and h.has_access(f, 'moderate')():
-                        num_moderate = DM.ForumPost.query.find({
-                            'discussion_id': f._id,
-                            'status': {'$ne': 'ok'},
-                            'deleted': False,
-                        }).count()
                         moderate_link = SitemapEntry(
                             'Moderate', "%smoderate/" % f.url(), ui_icon=g.icons['pencil'],
-                            small=num_moderate)
+                            small=DM.ForumPost.query.find({'discussion_id': f._id, 'status': {'$ne': 'ok'}}).count())
                     forum_links.append(
                         SitemapEntry(f.name, f.url(), small=f.num_topics))
             url = c.app.url + 'create_topic/'

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/import_support.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/import_support.py b/ForgeDiscussion/forgediscussion/import_support.py
index 5318ec5..a573b96 100644
--- a/ForgeDiscussion/forgediscussion/import_support.py
+++ b/ForgeDiscussion/forgediscussion/import_support.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -27,6 +31,7 @@ from pylons import tmpl_context as c
 from allura import model as M
 
 from forgediscussion import model as DM
+import collections
 
 log = logging.getLogger(__name__)
 
@@ -50,11 +55,11 @@ def perform_import(json, username_mapping, default_username=None, create_users=F
                     c.app.config.options.mount_point,
                     w)
 
-    for name, forum in json.forums.iteritems():
+    for name, forum in json.forums.items():
         log.info('... %s has %d threads with %d total posts',
-                 name, len(forum.threads), sum(len(t) for t in forum.threads.itervalues()))
+                 name, len(forum.threads), sum(len(t) for t in forum.threads.values()))
 
-    for name, forum in json.forums.iteritems():
+    for name, forum in json.forums.items():
         log.info('... creating %s/%s: %s',
                  c.project.shortname,
                  c.app.config.options.mount_point,
@@ -64,7 +69,7 @@ def perform_import(json, username_mapping, default_username=None, create_users=F
             name=forum['name'],
             shortname=forum['name'],
             description=forum['description'])
-        for tid, posts in forum.threads.iteritems():
+        for tid, posts in forum.threads.items():
             rest, head = posts[:-1], posts[-1]
             t = DM.ForumThread.new(
                 _id=tid,
@@ -124,7 +129,7 @@ class AlluraUser(S.FancySchemaItem):
         result = M.User.by_username(sf_username)
         if result is None:
             self.warnings.append('User %s not found' % value)
-            if callable(self.default_username):
+            if isinstance(self.default_username, collections.Callable):
                 sf_username = self.default_username(value)
             else:
                 sf_username = self.default_username

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/model/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/model/__init__.py b/ForgeDiscussion/forgediscussion/model/__init__.py
index 182bd94..9410354 100644
--- a/ForgeDiscussion/forgediscussion/model/__init__.py
+++ b/ForgeDiscussion/forgediscussion/model/__init__.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -15,4 +19,4 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-from forum import Forum, ForumFile, ForumThread, ForumPost, ForumAttachment
+from .forum import Forum, ForumFile, ForumThread, ForumPost, ForumAttachment

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/model/forum.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/model/forum.py b/ForgeDiscussion/forgediscussion/model/forum.py
index 96e570d..e6bbd4e 100644
--- a/ForgeDiscussion/forgediscussion/model/forum.py
+++ b/ForgeDiscussion/forgediscussion/model/forum.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/site_stats.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/site_stats.py b/ForgeDiscussion/forgediscussion/site_stats.py
index cabb5dd..2ee3f7e 100644
--- a/ForgeDiscussion/forgediscussion/site_stats.py
+++ b/ForgeDiscussion/forgediscussion/site_stats.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -22,6 +26,4 @@ from . import model as DM
 
 def posts_24hr():
     window = datetime.utcnow() - timedelta(hours=24)
-    q = {'timestamp': {'$gte': window},
-         'deleted': False}
-    return DM.ForumPost.query.find(q).count()
+    return DM.ForumPost.query.find({'timestamp': {'$gte': window}}).count()

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/tasks.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tasks.py b/ForgeDiscussion/forgediscussion/tasks.py
index 9566818..ddf3bb4 100644
--- a/ForgeDiscussion/forgediscussion/tasks.py
+++ b/ForgeDiscussion/forgediscussion/tasks.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
index 9c5397f..877524c 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
@@ -17,6 +17,10 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 import mock
 import random
 import logging
@@ -206,9 +210,8 @@ class TestForumAsync(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key(
-                    'value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params['subject'] = 'New Subject'
         params['text'] = 'Asdf'
         r = self.app.post(url, params=params)
@@ -218,9 +221,8 @@ class TestForumAsync(TestController):
         params = dict()
         inputs = post_form.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key(
-                    'value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[post_form.find('textarea')['name']] = 'text'
         r = self.app.post(url + 'reply', params=params)
         self._post('testforum', 'Test Reply', 'Nothing here, either',
@@ -289,7 +291,7 @@ class TestForum(TestController):
     @staticmethod
     def fill_thread_reply(r):
         form = r.forms['edit_post']
-        for field in form.fields.values():
+        for field in list(form.fields.values()):
             field = field[0]
             if field.id is None:
                 continue
@@ -300,7 +302,7 @@ class TestForum(TestController):
     @staticmethod
     def fill_new_topic_form(r):
         form = r.forms['create_new_topic']
-        for field in form.fields.values():
+        for field in list(form.fields.values()):
             field = field[0]
             if field.id is None:
                 continue
@@ -315,11 +317,11 @@ class TestForum(TestController):
 
     def test_unicode_name(self):
         r = self.app.get('/admin/discussion/forums')
-        r.forms[1]['add_forum.shortname'] = u'téstforum'.encode('utf-8')
-        r.forms[1]['add_forum.name'] = u'Tést Forum'.encode('utf-8')
+        r.forms[1]['add_forum.shortname'] = 'téstforum'.encode('utf-8')
+        r.forms[1]['add_forum.name'] = 'Tést Forum'.encode('utf-8')
         r.forms[1].submit()
         r = self.app.get('/admin/discussion/forums')
-        assert u'téstforum'.encode('utf-8') in r
+        assert 'téstforum'.encode('utf-8') in r
 
     def test_markdown_description(self):
         r = self.app.get('/admin/discussion/forums')
@@ -369,9 +371,8 @@ class TestForum(TestController):
             params = dict()
             inputs = f.findAll('input')
             for field in inputs:
-                if field.has_key('name'):
-                    params[field['name']] = field.has_key(
-                        'value') and field['value'] or ''
+                if 'name' in field:
+                    params[field['name']] = 'value' in field and field['value'] or ''
             params[f.find('textarea')['name']] = '1st post in Zero Posts thread'
             params[f.find('select')['name']] = 'testforum'
             params[f.find('input', {'style': 'width: 90%'})['name']] = 'Test Zero Posts'
@@ -413,8 +414,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'This is a *test thread*'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'Test Thread'
@@ -437,8 +438,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'Post text'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = "this is <h2> o'clock"
@@ -471,8 +472,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'Post content'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'Test Thread'
@@ -508,8 +509,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'aaa'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'AAA'
@@ -522,8 +523,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'bbb'
         thread = self.app.post(str(rep_url), params=params)
         thread = self.app.get(url)
@@ -544,8 +545,8 @@ class TestForum(TestController):
         params = dict()
         inputs = reply_form.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[reply_form.find('textarea')['name']] = 'zzz'
         self.app.post(post_link, params)
         r = self.app.get(thread_url)
@@ -559,8 +560,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'Post text'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'Post subject'
@@ -573,8 +574,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name') and 'subscription' not in field['name']:
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field and 'subscription' not in field['name']:
+                params[field['name']] = 'value' in field and field['value'] or ''
         self.app.post(str(subscribe_url), params=params)
         self.app.get('/discussion/general/subscribe_to_forum?subscribe=True')
         f = thread.html.find('div', {'class': 'row reply_post_form'}).find('form')
@@ -582,8 +583,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'Reply 2'
         self.app.post(str(rep_url), params=params)
         assert M.Notification.query.find(
@@ -607,8 +608,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'aaa aaa'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'AAAA'
@@ -660,8 +661,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'aaa aaa'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'topic1'
@@ -674,8 +675,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'aaa aaa'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'topic2'
@@ -725,8 +726,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'aaa aaa'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'topic1'
@@ -740,8 +741,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'bbb'
         thread = self.app.post(str(rep_url), params=params)
         thread = self.app.get(url)
@@ -773,8 +774,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'aaa'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'AAA'
@@ -797,8 +798,8 @@ class TestForum(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key('value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'aaa'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'AAA'
@@ -823,13 +824,13 @@ class TestForum(TestController):
 
     def test_create_topic_unicode(self):
         r = self.app.get('/admin/discussion/forums')
-        r.forms[1]['add_forum.shortname'] = u'téstforum'.encode('utf-8')
-        r.forms[1]['add_forum.name'] = u'Tést Forum'.encode('utf-8')
+        r.forms[1]['add_forum.shortname'] = 'téstforum'.encode('utf-8')
+        r.forms[1]['add_forum.name'] = 'Tést Forum'.encode('utf-8')
         r.forms[1].submit()
         r = self.app.get('/admin/discussion/forums')
-        assert u'téstforum'.encode('utf-8') in r
-        r = self.app.get(u'/p/test/discussion/create_topic/téstforum/'.encode('utf-8'))
-        assert u'<option value="téstforum" selected>Tést Forum</option>' in r
+        assert 'téstforum'.encode('utf-8') in r
+        r = self.app.get('/p/test/discussion/create_topic/téstforum/'.encode('utf-8'))
+        assert '<option value="téstforum" selected>Tést Forum</option>' in r
 
 
 class TestForumStats(TestController):

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
index 0eb4168..f425b54 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -17,7 +21,7 @@
 
 import os
 import allura
-from StringIO import StringIO
+from io import StringIO
 import logging
 
 import PIL
@@ -185,9 +189,8 @@ class TestForumAdmin(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key(
-                    'value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'secret text'
         params[f.find('select')['name']] = 'secret'
         params[f.find('input', {'style': 'width: 90%'})
@@ -240,9 +243,8 @@ class TestForumAdmin(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key(
-                    'value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'post text'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'post topic'
@@ -266,9 +268,8 @@ class TestForumAdmin(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key(
-                    'value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'post text'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'post topic'
@@ -296,9 +297,8 @@ class TestForumAdmin(TestController):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key(
-                    'value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = 'post text'
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'post topic'

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
index 73b0a8f..5c02a3f 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py b/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
index 1b7d080..470ce43 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
@@ -17,6 +17,10 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 from nose.tools import assert_equal, assert_in
 
 from allura.lib import helpers as h
@@ -54,9 +58,8 @@ class TestDiscussionApiBase(TestRestApiBase):
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
-            if field.has_key('name'):
-                params[field['name']] = field.has_key(
-                    'value') and field['value'] or ''
+            if 'name' in field:
+                params[field['name']] = 'value' in field and field['value'] or ''
         params[f.find('textarea')['name']] = text
         params[f.find('select')['name']] = forum
         params[f.find('input', {'style': 'width: 90%'})['name']] = subject
@@ -79,8 +82,8 @@ class TestRootRestController(TestDiscussionApiBase):
         assert_equal(forums[0]['last_post']['subject'], 'Hi guys')
         assert_equal(forums[0]['last_post']['author'], 'test-admin')
         assert_equal(forums[0]['last_post']['text'], 'Hi boys and girls')
-        assert_equal(forums[1]['name'], u'Say Héllo')
-        assert_equal(forums[1]['description'], u'Say héllo here')
+        assert_equal(forums[1]['name'], 'Say Héllo')
+        assert_equal(forums[1]['description'], 'Say héllo here')
         assert_equal(forums[1]['num_topics'], 0)
         assert_equal(
             forums[1]['url'], 'http://localhost/rest/p/test/discussion/h%C3%A9llo/')
@@ -120,7 +123,7 @@ class TestRootRestController(TestDiscussionApiBase):
         self.create_topic('general', 'Hi again', 'It should not be shown')
         t = ForumThread.query.find({'subject': 'Hi again'}).first()
         first_post = t.first_post
-        first_post.status = u'pending'
+        first_post.status = 'pending'
         first_post.commit()
         forum = self.api_get('/rest/p/test/discussion/general/')
         forum = forum.json['forum']
@@ -155,7 +158,7 @@ class TestRootRestController(TestDiscussionApiBase):
         resp = self.app.get('/rest/p/test/discussion/?limit=1&page=1')
         forums = resp.json['forums']
         assert_equal(len(forums), 1)
-        assert_equal(forums[0]['name'], u'Say Héllo')
+        assert_equal(forums[0]['name'], 'Say Héllo')
         assert_equal(resp.json['count'], 2)
         assert_equal(resp.json['page'], 1)
         assert_equal(resp.json['limit'], 1)
@@ -236,7 +239,7 @@ class TestRootRestController(TestDiscussionApiBase):
     def test_private_forums(self):
         r = self.app.get('/p/test/admin/discussion/forums')
         form = r.forms[0]
-        if form['forum-0.shortname'].value == u'héllo':
+        if form['forum-0.shortname'].value == 'héllo':
             form['forum-0.members_only'] = True
         else:
             form['forum-1.members_only'] = True

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/tests/test_app.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/test_app.py b/ForgeDiscussion/forgediscussion/tests/test_app.py
index 76f2071..3006510 100644
--- a/ForgeDiscussion/forgediscussion/tests/test_app.py
+++ b/ForgeDiscussion/forgediscussion/tests/test_app.py
@@ -19,6 +19,10 @@
 
 #-*- python -*-
 
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 import tempfile
 import json
 
@@ -44,19 +48,19 @@ class TestBulkExport(TestDiscussionApiBase):
         discussion = json.loads(f.read())
         forums = sorted(discussion['forums'], key=lambda x: x['name'])
 
-        assert_equal(forums[0]['shortname'], u'general')
+        assert_equal(forums[0]['shortname'], 'general')
         assert_equal(
-            forums[0]['description'], u'Forum about anything you want to talk about.')
-        assert_equal(forums[0]['name'], u'General Discussion')
+            forums[0]['description'], 'Forum about anything you want to talk about.')
+        assert_equal(forums[0]['name'], 'General Discussion')
         forums[0]['threads'] = sorted(forums[0]['threads'],
                                       key=lambda x: x['posts'][0]['subject'])
         assert_equal(
-            forums[0]['threads'][0]['posts'][0]['text'], u'Hi boys and girls')
+            forums[0]['threads'][0]['posts'][0]['text'], 'Hi boys and girls')
         assert_equal(
-            forums[0]['threads'][0]['posts'][0]['subject'], u'Hi guys')
-        assert_equal(forums[0]['threads'][1]['posts'][0]['text'], u'1st post')
+            forums[0]['threads'][0]['posts'][0]['subject'], 'Hi guys')
+        assert_equal(forums[0]['threads'][1]['posts'][0]['text'], '1st post')
         assert_equal(
-            forums[0]['threads'][1]['posts'][0]['subject'], u"Let's talk")
-        assert_equal(forums[1]['shortname'], u'héllo')
-        assert_equal(forums[1]['description'], u'Say héllo here')
-        assert_equal(forums[1]['name'], u'Say Héllo')
+            forums[0]['threads'][1]['posts'][0]['subject'], "Let's talk")
+        assert_equal(forums[1]['shortname'], 'héllo')
+        assert_equal(forums[1]['description'], 'Say héllo here')
+        assert_equal(forums[1]['name'], 'Say Héllo')

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py b/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
index 44d6989..e991768 100644
--- a/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
+++ b/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/utils.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/utils.py b/ForgeDiscussion/forgediscussion/utils.py
index d9128d9..adc90d6 100644
--- a/ForgeDiscussion/forgediscussion/utils.py
+++ b/ForgeDiscussion/forgediscussion/utils.py
@@ -16,6 +16,10 @@
 #       under the License.
 
 """ ForgeDiscussion utilities. """
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 
 from bson import ObjectId
 from tg import flash

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/version.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/version.py b/ForgeDiscussion/forgediscussion/version.py
index 1b493f8..8f5910c 100644
--- a/ForgeDiscussion/forgediscussion/version.py
+++ b/ForgeDiscussion/forgediscussion/version.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/widgets/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/widgets/__init__.py b/ForgeDiscussion/forgediscussion/widgets/__init__.py
index f733ddb..87995cd 100644
--- a/ForgeDiscussion/forgediscussion/widgets/__init__.py
+++ b/ForgeDiscussion/forgediscussion/widgets/__init__.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -15,6 +19,6 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-from forum_widgets import ForumSubscriptionForm, ThreadSubscriptionForm, AnnouncementsTable
-from forum_widgets import ModerateThread, ForumHeader, ThreadHeader
-from forum_widgets import Post, Thread, Forum
+from .forum_widgets import ForumSubscriptionForm, ThreadSubscriptionForm, AnnouncementsTable
+from .forum_widgets import ModerateThread, ForumHeader, ThreadHeader
+from .forum_widgets import Post, Thread, Forum

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/widgets/admin.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/widgets/admin.py b/ForgeDiscussion/forgediscussion/widgets/admin.py
index 696f155..5d08989 100644
--- a/ForgeDiscussion/forgediscussion/widgets/admin.py
+++ b/ForgeDiscussion/forgediscussion/widgets/admin.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -67,7 +71,7 @@ class AddForum(ff.AdminForm):
                          validator=fev.UnicodeString()),
             ew.TextField(name='shortname', label='Short Name',
                          validator=All(
-                             fev.Regex(ur"^[^\s\/\.]*$", not_empty=True, messages={
+                             fev.Regex(r"^[^\s\/\.]*$", not_empty=True, messages={
                                  'invalid': 'Shortname cannot contain space . or /',
                                  'empty': 'You must create a short name for the forum.'}),
                              UniqueForumShortnameValidator())),

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/forgediscussion/widgets/forum_widgets.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/widgets/forum_widgets.py b/ForgeDiscussion/forgediscussion/widgets/forum_widgets.py
index d26f330..8f7ca83 100644
--- a/ForgeDiscussion/forgediscussion/widgets/forum_widgets.py
+++ b/ForgeDiscussion/forgediscussion/widgets/forum_widgets.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeDiscussion/setup.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/setup.py b/ForgeDiscussion/setup.py
index 165ce4b..93faf76 100644
--- a/ForgeDiscussion/setup.py
+++ b/ForgeDiscussion/setup.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/controllers.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/controllers.py b/ForgeGit/forgegit/controllers.py
index 93e9364..2247772 100644
--- a/ForgeGit/forgegit/controllers.py
+++ b/ForgeGit/forgegit/controllers.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/git_main.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/git_main.py b/ForgeGit/forgegit/git_main.py
index 62fc0f7..0a2f32b 100644
--- a/ForgeGit/forgegit/git_main.py
+++ b/ForgeGit/forgegit/git_main.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/model/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/model/__init__.py b/ForgeGit/forgegit/model/__init__.py
index 72eb80a..ec1caf1 100644
--- a/ForgeGit/forgegit/model/__init__.py
+++ b/ForgeGit/forgegit/model/__init__.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -15,4 +19,4 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-from git_repo import Repository
+from .git_repo import Repository

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/model/git_repo.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/model/git_repo.py b/ForgeGit/forgegit/model/git_repo.py
index aeb5734..6aa860f 100644
--- a/ForgeGit/forgegit/model/git_repo.py
+++ b/ForgeGit/forgegit/model/git_repo.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -126,7 +130,7 @@ class Repository(M.Repository):
         author = h.really_unicode(c.user.display_name or c.user.username)
         tmp_repo.git.config('user.name', author)
         tmp_repo.git.config('user.email', '')
-        msg = u'Merge {} branch {} into {}\n\n{}'.format(
+        msg = 'Merge {} branch {} into {}\n\n{}'.format(
             mr.downstream_repo.url(),
             mr.source_branch,
             mr.target_branch,
@@ -160,7 +164,7 @@ class GitImplementation(M.RepositoryImplementation):
             _git = git.Repo(self._repo.full_fs_path, odbt=git.GitCmdObjectDB)
             _git.git = GitLibCmdWrapper(_git.git)
             return _git
-        except (git.exc.NoSuchPathError, git.exc.InvalidGitRepositoryError), err:
+        except (git.exc.NoSuchPathError, git.exc.InvalidGitRepositoryError) as err:
             log.error('Problem looking up repo: %r', err)
             return None
 
@@ -474,7 +478,7 @@ class GitImplementation(M.RepositoryImplementation):
                           'hooks', 'post-receive')
         with open(fn, 'w') as fp:
             fp.write(text)
-        os.chmod(fn, 0755)
+        os.chmod(fn, 0o755)
 
     def _object(self, oid):
         evens = oid[::2]

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/tests/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/__init__.py b/ForgeGit/forgegit/tests/__init__.py
index b693039..3e7d4a9 100644
--- a/ForgeGit/forgegit/tests/__init__.py
+++ b/ForgeGit/forgegit/tests/__init__.py
@@ -19,6 +19,10 @@
 
 
 # Make our own Git tool test decorator
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 from allura.tests.decorators import with_tool
 
 with_git = with_tool('test', 'Git', 'src-git', 'Git', type='git')

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/tests/functional/test_auth.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/functional/test_auth.py b/ForgeGit/forgegit/tests/functional/test_auth.py
index 1a230d4..bbd7380 100644
--- a/ForgeGit/forgegit/tests/functional/test_auth.py
+++ b/ForgeGit/forgegit/tests/functional/test_auth.py
@@ -17,6 +17,10 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 import json
 from datadiff.tools import assert_equal
 

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/tests/functional/test_controllers.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index 9de228a..3b9d033 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -113,12 +117,12 @@ class TestRootController(_TestCase):
         assert data['next_column'] == 1
         assert_equal(
             data['built_tree']['df30427c488aeab84b2352bdf88a3b19223f9d7a'],
-            {u'url': u'/p/test/src-git/ci/df30427c488aeab84b2352bdf88a3b19223f9d7a/',
-             u'oid': u'df30427c488aeab84b2352bdf88a3b19223f9d7a',
-             u'short_id': u'[df3042]',
-             u'column': 0,
-             u'parents': [u'6a45885ae7347f1cac5103b0050cc1be6a1496c8'],
-             u'message': u'Add README', u'row': 2})
+            {'url': '/p/test/src-git/ci/df30427c488aeab84b2352bdf88a3b19223f9d7a/',
+             'oid': 'df30427c488aeab84b2352bdf88a3b19223f9d7a',
+             'short_id': '[df3042]',
+             'column': 0,
+             'parents': ['6a45885ae7347f1cac5103b0050cc1be6a1496c8'],
+             'message': 'Add README', 'row': 2})
 
     def test_log(self):
         resp = self.app.get(
@@ -474,9 +478,9 @@ class TestFork(_TestCase):
 
     def _follow(self, r, **kw):
         if r.status_int == 302:
-            print r.request.url
+            print(r.request.url)
         while r.status_int == 302:
-            print ' ==> 302 ==> %s' % r.location
+            print(' ==> 302 ==> %s' % r.location)
             r = r.follow(**kw)
         return r
 
@@ -670,7 +674,7 @@ class TestFork(_TestCase):
         assert '<p>changed description</p' in r
         assert 'Merge Request #1: changed summary (open)' in r
         changes = r.html.findAll('div', attrs={'class': 'markdown_content'})[-1]
-        dd_assert_equal(unicode(changes), """
+        dd_assert_equal(str(changes), """
 <div class="markdown_content"><ul>
 <li>
 <p><strong>Summary</strong>: summary --&gt; changed summary</p>

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index e516f35..ba404d2 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -17,6 +17,10 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 import os
 import shutil
 import stat
@@ -29,7 +33,7 @@ from pylons import tmpl_context as c, app_globals as g
 import tg
 from ming.base import Object
 from ming.orm import ThreadLocalORMSession, session
-from nose.tools import assert_equal
+from nose.tools import assert_equal, assert_in
 from testfixtures import TempDirectory
 from datadiff.tools import assert_equals
 
@@ -259,49 +263,49 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         entries = list(self.repo.log(id_only=False))
         assert_equal(entries, [
             {'authored': {'date': datetime.datetime(2010, 10, 7, 18, 44, 11),
-                          'email': u'rcopeland@geek.net',
-                          'name': u'Rick Copeland'},
+                          'email': 'rcopeland@geek.net',
+                          'name': 'Rick Copeland'},
              'committed': {'date': datetime.datetime(2010, 10, 7, 18, 44, 11),
-                           'email': u'rcopeland@geek.net',
-                           'name': u'Rick Copeland'},
+                           'email': 'rcopeland@geek.net',
+                           'name': 'Rick Copeland'},
              'id': '1e146e67985dcd71c74de79613719bef7bddca4a',
-             'message': u'Change README\n',
+             'message': 'Change README\n',
              'parents': ['df30427c488aeab84b2352bdf88a3b19223f9d7a'],
              'refs': ['HEAD', 'foo', 'master'],
              'size': None,
              'rename_details': {}},
             {'authored': {'date': datetime.datetime(2010, 10, 7, 18, 44, 1),
-                          'email': u'rcopeland@geek.net',
-                          'name': u'Rick Copeland'},
+                          'email': 'rcopeland@geek.net',
+                          'name': 'Rick Copeland'},
              'committed': {'date': datetime.datetime(2010, 10, 7, 18, 44, 1),
-                           'email': u'rcopeland@geek.net',
-                           'name': u'Rick Copeland'},
+                           'email': 'rcopeland@geek.net',
+                           'name': 'Rick Copeland'},
              'id': 'df30427c488aeab84b2352bdf88a3b19223f9d7a',
-             'message': u'Add README\n',
+             'message': 'Add README\n',
              'parents': ['6a45885ae7347f1cac5103b0050cc1be6a1496c8'],
              'refs': [],
              'size': None,
              'rename_details': {}},
             {'authored': {'date': datetime.datetime(2010, 10, 7, 18, 43, 26),
-                          'email': u'rcopeland@geek.net',
-                          'name': u'Rick Copeland'},
+                          'email': 'rcopeland@geek.net',
+                          'name': 'Rick Copeland'},
              'committed': {'date': datetime.datetime(2010, 10, 7, 18, 43, 26),
-                           'email': u'rcopeland@geek.net',
-                           'name': u'Rick Copeland'},
+                           'email': 'rcopeland@geek.net',
+                           'name': 'Rick Copeland'},
              'id': '6a45885ae7347f1cac5103b0050cc1be6a1496c8',
-             'message': u'Remove file\n',
+             'message': 'Remove file\n',
              'parents': ['9a7df788cf800241e3bb5a849c8870f2f8259d98'],
              'refs': [],
              'size': None,
              'rename_details': {}},
             {'authored': {'date': datetime.datetime(2010, 10, 7, 18, 42, 54),
-                          'email': u'rcopeland@geek.net',
-                          'name': u'Rick Copeland'},
+                          'email': 'rcopeland@geek.net',
+                          'name': 'Rick Copeland'},
              'committed': {'date': datetime.datetime(2010, 10, 7, 18, 42, 54),
-                           'email': u'rcopeland@geek.net',
-                           'name': u'Rick Copeland'},
+                           'email': 'rcopeland@geek.net',
+                           'name': 'Rick Copeland'},
              'id': '9a7df788cf800241e3bb5a849c8870f2f8259d98',
-             'message': u'Initial commit\n',
+             'message': 'Initial commit\n',
              'parents': [],
              'refs': [],
              'size': None,
@@ -309,32 +313,32 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         ])
 
     def test_log_unicode(self):
-        entries = list(self.repo.log(path=u'völundr', id_only=False))
+        entries = list(self.repo.log(path='völundr', id_only=False))
         assert_equal(entries, [])
 
     def test_log_file(self):
         entries = list(self.repo.log(path='README', id_only=False))
         assert_equal(entries, [
             {'authored': {'date': datetime.datetime(2010, 10, 7, 18, 44, 11),
-                          'email': u'rcopeland@geek.net',
-                          'name': u'Rick Copeland'},
+                          'email': 'rcopeland@geek.net',
+                          'name': 'Rick Copeland'},
              'committed': {'date': datetime.datetime(2010, 10, 7, 18, 44, 11),
-                           'email': u'rcopeland@geek.net',
-                           'name': u'Rick Copeland'},
+                           'email': 'rcopeland@geek.net',
+                           'name': 'Rick Copeland'},
              'id': '1e146e67985dcd71c74de79613719bef7bddca4a',
-             'message': u'Change README\n',
+             'message': 'Change README\n',
              'parents': ['df30427c488aeab84b2352bdf88a3b19223f9d7a'],
              'refs': ['HEAD', 'foo', 'master'],
              'size': 28,
              'rename_details': {}},
             {'authored': {'date': datetime.datetime(2010, 10, 7, 18, 44, 1),
-                          'email': u'rcopeland@geek.net',
-                          'name': u'Rick Copeland'},
+                          'email': 'rcopeland@geek.net',
+                          'name': 'Rick Copeland'},
              'committed': {'date': datetime.datetime(2010, 10, 7, 18, 44, 1),
-                           'email': u'rcopeland@geek.net',
-                           'name': u'Rick Copeland'},
+                           'email': 'rcopeland@geek.net',
+                           'name': 'Rick Copeland'},
              'id': 'df30427c488aeab84b2352bdf88a3b19223f9d7a',
-             'message': u'Add README\n',
+             'message': 'Add README\n',
              'parents': ['6a45885ae7347f1cac5103b0050cc1be6a1496c8'],
              'refs': [],
              'size': 15,
@@ -368,15 +372,15 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         send_notifications(
             self.repo, ['1e146e67985dcd71c74de79613719bef7bddca4a', ])
         ThreadLocalORMSession.flush_all()
-        n = M.Notification.query.find(
-            dict(subject='[test:src-git] [1e146e] - Rick Copeland: Change README')).first()
+
+        n = M.Notification.query.find({'subject': '[test:src-git] New commit by Rick Copeland'}).first()
         assert n
-        assert 'master: ' in n.text, n.text
+        assert_in('```Change README```', n.text)
         send_notifications(
             self.repo, ['1e146e67985dcd71c74de79613719bef7bddca4a', 'df30427c488aeab84b2352bdf88a3b19223f9d7a'])
         ThreadLocalORMSession.flush_all()
         assert M.Notification.query.find(
-            dict(subject='[test:src-git] 2 new commits to Test Project Git')).first()
+            dict(subject= '[test:src-git] 2 new commits to Git')).first()
 
     def test_tarball(self):
         tmpdir = tg.config['scm.repos.tarball.root']
@@ -407,17 +411,17 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         c.lcid_cache = {}  # else it'll be a mock
         lcd_map = self.repo.commit('HEAD').tree.ls()
         self.assertEqual(lcd_map, [{
-            'href': u'README',
+            'href': 'README',
             'kind': 'BLOB',
             'last_commit': {
-                    'author': u'Rick Copeland',
-                'author_email': u'rcopeland@geek.net',
+                    'author': 'Rick Copeland',
+                'author_email': 'rcopeland@geek.net',
                 'author_url': None,
                 'date': datetime.datetime(2010, 10, 7, 18, 44, 11),
-                'href': u'/p/test/src-git/ci/1e146e67985dcd71c74de79613719bef7bddca4a/',
-                'shortlink': u'[1e146e]',
-                'summary': u'Change README'},
-            'name': u'README'}])
+                'href': '/p/test/src-git/ci/1e146e67985dcd71c74de79613719bef7bddca4a/',
+                'shortlink': '[1e146e]',
+                'summary': 'Change README'},
+            'name': 'README'}])
 
     def test_tarball_status(self):
         tmpdir = tg.config['scm.repos.tarball.root']
@@ -543,44 +547,44 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         payload = sender.get_payload(commit_ids=cids, ref='refs/heads/zz')
         expected_payload = {
             'size': 2,
-            'ref': u'refs/heads/zz',
-            'after': u'5c47243c8e424136fd5cdd18cd94d34c66d1955c',
-            'before': u'df30427c488aeab84b2352bdf88a3b19223f9d7a',
+            'ref': 'refs/heads/zz',
+            'after': '5c47243c8e424136fd5cdd18cd94d34c66d1955c',
+            'before': 'df30427c488aeab84b2352bdf88a3b19223f9d7a',
             'commits': [{
-                'id': u'5c47243c8e424136fd5cdd18cd94d34c66d1955c',
-                'url': u'http://localhost/p/test/src-git/ci/5c47243c8e424136fd5cdd18cd94d34c66d1955c/',
+                'id': '5c47243c8e424136fd5cdd18cd94d34c66d1955c',
+                'url': 'http://localhost/p/test/src-git/ci/5c47243c8e424136fd5cdd18cd94d34c66d1955c/',
                 'timestamp': datetime.datetime(2013, 3, 28, 18, 54, 16),
-                'message': u'Not repo root',
-                'author': {'name': u'Cory Johns',
-                           'email': u'cjohns@slashdotmedia.com',
+                'message': 'Not repo root',
+                'author': {'name': 'Cory Johns',
+                           'email': 'cjohns@slashdotmedia.com',
                            'username': 'cory'},
-                'committer': {'name': u'Cory Johns',
-                              'email': u'cjohns@slashdotmedia.com',
+                'committer': {'name': 'Cory Johns',
+                              'email': 'cjohns@slashdotmedia.com',
                               'username': 'cory'},
-                'added': [u'bad'],
+                'added': ['bad'],
                 'removed': [],
                 'modified': [],
                 'copied': []
             }, {
-                'id': u'1e146e67985dcd71c74de79613719bef7bddca4a',
-                'url': u'http://localhost/p/test/src-git/ci/1e146e67985dcd71c74de79613719bef7bddca4a/',
+                'id': '1e146e67985dcd71c74de79613719bef7bddca4a',
+                'url': 'http://localhost/p/test/src-git/ci/1e146e67985dcd71c74de79613719bef7bddca4a/',
                 'timestamp': datetime.datetime(2010, 10, 7, 18, 44, 11),
-                'message': u'Change README',
-                'author': {'name': u'Rick Copeland',
-                           'email': u'rcopeland@geek.net',
+                'message': 'Change README',
+                'author': {'name': 'Rick Copeland',
+                           'email': 'rcopeland@geek.net',
                            'username': 'rick'},
-                'committer': {'name': u'Rick Copeland',
-                              'email': u'rcopeland@geek.net',
+                'committer': {'name': 'Rick Copeland',
+                              'email': 'rcopeland@geek.net',
                               'username': 'rick'},
                 'added': [],
                 'removed': [],
-                'modified': [u'README'],
+                'modified': ['README'],
                 'copied': []
             }],
             'repository': {
-                'name': u'Git',
-                'full_name': u'/p/test/src-git/',
-                'url': u'http://localhost/p/test/src-git/',
+                'name': 'Git',
+                'full_name': '/p/test/src-git/',
+                'url': 'http://localhost/p/test/src-git/',
             },
         }
         assert_equals(payload, expected_payload)
@@ -632,8 +636,8 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
             tmp_repo.git.config.call_args_list,
             [mock.call('user.name', 'Test Admin'),
              mock.call('user.email', '')])
-        msg = u'Merge downstream-repo-url branch source-branch into target-branch'
-        msg += u'\n\nhttp://localhost/merge-request/1/'
+        msg = 'Merge downstream-repo-url branch source-branch into target-branch'
+        msg += '\n\nhttp://localhost/merge-request/1/'
         tmp_repo.git.merge.assert_called_once_with('cid', '-m', msg)
         tmp_repo.git.push.assert_called_once_with('origin', 'target-branch')
         shutil.rmtree.assert_called_once_with(
@@ -659,7 +663,7 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         # spaces and unicode filenames
         diffs = repo.paged_diffs('407950e8fba4dbc108ffbce0128ed1085c52cfd7')
         expected = {
-            'added': [u'with space.txt', u'привіт.txt'],
+            'added': ['with space.txt', 'привіт.txt'],
             'removed': [],
             'changed': [],
             'total': 2,
@@ -670,7 +674,7 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         expected = {
             'added': [],
             'removed': [],
-            'changed': [u'привіт.txt'],
+            'changed': ['привіт.txt'],
             'total': 1,
         }
         assert_equals(diffs, expected)
@@ -678,7 +682,7 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         # initial commit is special, but must work too
         diffs = repo.paged_diffs('afaa6d93eb5661fb04f8e10e9ba1039b7441a6c7')
         expected = {
-            'added': [u'README.md'],
+            'added': ['README.md'],
             'removed': [],
             'changed': [],
             'total': 1,
@@ -708,14 +712,14 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         expected = [
             {'authored': {
                 'date': datetime.datetime(2013, 3, 28, 18, 54, 16),
-                'email': u'cjohns@slashdotmedia.com',
-                'name': u'Cory Johns'},
+                'email': 'cjohns@slashdotmedia.com',
+                'name': 'Cory Johns'},
              'committed': {
                  'date': datetime.datetime(2013, 3, 28, 18, 54, 16),
-                 'email': u'cjohns@slashdotmedia.com',
-                 'name': u'Cory Johns'},
+                 'email': 'cjohns@slashdotmedia.com',
+                 'name': 'Cory Johns'},
              'id': '5c47243c8e424136fd5cdd18cd94d34c66d1955c',
-             'message': u'Not repo root\n',
+             'message': 'Not repo root\n',
              'parents': ['1e146e67985dcd71c74de79613719bef7bddca4a'],
              'refs': ['zz'],
              'rename_details': {},
@@ -818,7 +822,7 @@ class TestGitCommit(unittest.TestCase):
                  + self.rev.diffs.changed
                  + self.rev.diffs.copied)
         for d in diffs:
-            print d
+            print(d)
 
     def test_log(self):
         # path only
@@ -920,5 +924,5 @@ class TestGitRename(unittest.TestCase):
 
     def test_merge_commit(self):
         merge_sha = '13951944969cf45a701bf90f83647b309815e6d5'
-        commit = self.repo.log(revs=merge_sha, id_only=False).next()
+        commit = next(self.repo.log(revs=merge_sha, id_only=False))
         self.assertEqual(commit['rename_details'], {})

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/tests/test_git_app.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/test_git_app.py b/ForgeGit/forgegit/tests/test_git_app.py
index 0566249..501ec29 100644
--- a/ForgeGit/forgegit/tests/test_git_app.py
+++ b/ForgeGit/forgegit/tests/test_git_app.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/tests/test_tasks.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/test_tasks.py b/ForgeGit/forgegit/tests/test_tasks.py
index 3f003d6..16d9db1 100644
--- a/ForgeGit/forgegit/tests/test_tasks.py
+++ b/ForgeGit/forgegit/tests/test_tasks.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/forgegit/version.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/version.py b/ForgeGit/forgegit/version.py
index 1b493f8..8f5910c 100644
--- a/ForgeGit/forgegit/version.py
+++ b/ForgeGit/forgegit/version.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeGit/setup.py
----------------------------------------------------------------------
diff --git a/ForgeGit/setup.py b/ForgeGit/setup.py
index e91068b..19c3bb4 100644
--- a/ForgeGit/setup.py
+++ b/ForgeGit/setup.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/docs/conf.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/docs/conf.py b/ForgeImporters/docs/conf.py
index 7783282..a3f967e 100644
--- a/ForgeImporters/docs/conf.py
+++ b/ForgeImporters/docs/conf.py
@@ -37,6 +37,10 @@
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx',
               'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig']
 
@@ -188,8 +192,8 @@ htmlhelp_basename = 'alluradoc'
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-    ('index', 'allura.tex', u'allura Documentation',
-     u'Cory Johns, Tim Van Steenburgh, Dave Brondsema', 'manual'),
+    ('index', 'allura.tex', 'allura Documentation',
+     'Cory Johns, Tim Van Steenburgh, Dave Brondsema', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index 8428142..0033577 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -18,16 +22,16 @@
 import os
 import errno
 import logging
-import urllib
-import urllib2
+import urllib.request, urllib.parse, urllib.error
+import urllib.request, urllib.error, urllib.parse
 from collections import defaultdict
 import traceback
-from urlparse import urlparse
+from urllib.parse import urlparse
 from datetime import datetime
 try:
-    from cStringIO import StringIO
+    from io import StringIO
 except ImportError:
-    from StringIO import StringIO
+    from io import StringIO
 
 from BeautifulSoup import BeautifulSoup
 from tg import expose, validate, flash, redirect, config
@@ -167,7 +171,7 @@ class ProjectExtractor(object):
 
     @staticmethod
     def urlopen(url, retries=3, codes=(408,), **kw):
-        req = urllib2.Request(url, **kw)
+        req = urllib.request.Request(url, **kw)
         req.add_header(
             'User-Agent', 'Allura Data Importer (https://allura.apache.org/)')
         return h.urlopen(req, retries=retries, codes=codes)
@@ -207,7 +211,7 @@ class ProjectExtractor(object):
 
         """
         return self.PAGE_MAP[page_name].format(
-            project_name=urllib.quote(self.project_name), **kw)
+            project_name=urllib.parse.quote(self.project_name), **kw)
 
     def parse_page(self, page):
         """Transforms the result of a `urlopen` call before returning it from
@@ -351,11 +355,10 @@ class ToolImportControllerMeta(type):
         return type.__call__(cls, importer, *args, **kw)
 
 
-class ToolImportController(BaseController):
+class ToolImportController(BaseController, metaclass=ToolImportControllerMeta):
     """ Base class for ToolImporter controllers.
 
     """
-    __metaclass__ = ToolImportControllerMeta
 
     def __init__(self, importer):
         """
@@ -392,7 +395,7 @@ class ToolImporterMeta(type):
         return type.__call__(cls, *args, **kw)
 
 
-class ToolImporter(object):
+class ToolImporter(object, metaclass=ToolImporterMeta):
 
     """
     Base class for tool importers.
@@ -428,7 +431,6 @@ class ToolImporter(object):
        The controller for this importer, to handle single tool imports.
 
     """
-    __metaclass__ = ToolImporterMeta
 
     target_app = None  # app or list of apps
     source = None  # string description of source, must match project importer

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/forge/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/forge/tracker.py b/ForgeImporters/forgeimporters/forge/tracker.py
index 2937b63..1cda5c8 100644
--- a/ForgeImporters/forgeimporters/forge/tracker.py
+++ b/ForgeImporters/forgeimporters/forge/tracker.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/github/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/__init__.py b/ForgeImporters/forgeimporters/github/__init__.py
index fe501dd..38bc4c7 100644
--- a/ForgeImporters/forgeimporters/github/__init__.py
+++ b/ForgeImporters/forgeimporters/github/__init__.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -19,7 +23,7 @@ import re
 import logging
 import json
 import time
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 from datetime import datetime
 
 from tg import config, session, redirect, request, expose
@@ -91,7 +95,7 @@ class GitHubProjectExtractor(base.ProjectExtractor):
         try:
             resp = super(GitHubProjectExtractor, self).urlopen(
                 self.add_token(url), **kw)
-        except urllib2.HTTPError as e:
+        except urllib.error.HTTPError as e:
             # GitHub will return 403 if rate limit exceeded.
             # We're checking for limit on every request below, but we still
             # can get 403 if other import task exceeds the limit before.

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/github/code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/code.py b/ForgeImporters/forgeimporters/github/code.py
index 410b3bb..d7707db 100644
--- a/ForgeImporters/forgeimporters/github/code.py
+++ b/ForgeImporters/forgeimporters/github/code.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/github/project.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/project.py b/ForgeImporters/forgeimporters/github/project.py
index c4d8c61..56e8b93 100644
--- a/ForgeImporters/forgeimporters/github/project.py
+++ b/ForgeImporters/forgeimporters/github/project.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/github/tasks.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tasks.py b/ForgeImporters/forgeimporters/github/tasks.py
index d74a9cd..eb584de 100644
--- a/ForgeImporters/forgeimporters/github/tasks.py
+++ b/ForgeImporters/forgeimporters/github/tasks.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/github/tests/test_code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tests/test_code.py b/ForgeImporters/forgeimporters/github/tests/test_code.py
index e247a5f..1c47352 100644
--- a/ForgeImporters/forgeimporters/github/tests/test_code.py
+++ b/ForgeImporters/forgeimporters/github/tests/test_code.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -96,12 +100,12 @@ class TestGitHubImportController(TestController, TestCase):
             r.location, 'http://localhost/p/{}/admin/'.format(
                 test_project_with_repo))
         self.assertEqual(
-            u'mymount', import_tool.post.call_args[1]['mount_point'])
+            'mymount', import_tool.post.call_args[1]['mount_point'])
         self.assertEqual(
-            u'mylabel', import_tool.post.call_args[1]['mount_label'])
+            'mylabel', import_tool.post.call_args[1]['mount_label'])
         self.assertEqual(
-            u'poop', import_tool.post.call_args[1]['project_name'])
-        self.assertEqual(u'spooky', import_tool.post.call_args[1]['user_name'])
+            'poop', import_tool.post.call_args[1]['project_name'])
+        self.assertEqual('spooky', import_tool.post.call_args[1]['user_name'])
         self.assertEqual(requests.head.call_count, 1)
 
     @with_git

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/github/tests/test_oauth.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tests/test_oauth.py b/ForgeImporters/forgeimporters/github/tests/test_oauth.py
index 584b403..1d40c51 100644
--- a/ForgeImporters/forgeimporters/github/tests/test_oauth.py
+++ b/ForgeImporters/forgeimporters/github/tests/test_oauth.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/github/tests/test_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tests/test_tracker.py b/ForgeImporters/forgeimporters/github/tests/test_tracker.py
index ba09ece..2d359c9 100644
--- a/ForgeImporters/forgeimporters/github/tests/test_tracker.py
+++ b/ForgeImporters/forgeimporters/github/tests/test_tracker.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -58,12 +62,12 @@ class TestGitHubTrackerImportController(TestController, TestCase):
         self.assertEqual(r.location, 'http://localhost/p/%s/admin/' %
                          test_project_with_tracker)
         self.assertEqual(
-            u'Issues', import_tool.post.call_args[1]['mount_label'])
+            'Issues', import_tool.post.call_args[1]['mount_label'])
         self.assertEqual(
-            u'issues', import_tool.post.call_args[1]['mount_point'])
+            'issues', import_tool.post.call_args[1]['mount_point'])
         self.assertEqual(
-            u'mulder', import_tool.post.call_args[1]['project_name'])
-        self.assertEqual(u'spooky', import_tool.post.call_args[1]['user_name'])
+            'mulder', import_tool.post.call_args[1]['project_name'])
+        self.assertEqual('spooky', import_tool.post.call_args[1]['user_name'])
         self.assertEqual(requests.head.call_count, 1)
 
     @with_tracker

http://git-wip-us.apache.org/repos/asf/allura/blob/d52f8e2a/ForgeImporters/forgeimporters/github/tests/test_utils.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tests/test_utils.py b/ForgeImporters/forgeimporters/github/tests/test_utils.py
index 6ce7821..411e01d 100644
--- a/ForgeImporters/forgeimporters/github/tests/test_utils.py
+++ b/ForgeImporters/forgeimporters/github/tests/test_utils.py
@@ -1,3 +1,7 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
 #       Licensed to the Apache Software Foundation (ASF) under one
 #       or more contributor license agreements.  See the NOTICE file
 #       distributed with this work for additional information
@@ -86,12 +90,12 @@ class TestGitHubMarkdownConverter(object):
         assert_equal(self.conv.convert(text), '<s>mistake</s>')
 
     def test_inline_code_block(self):
-        text = u'This `~~some text~~` converts to this ~~strike out~~.'
-        result = u'This `~~some text~~` converts to this <s>strike out</s>.'
+        text = 'This `~~some text~~` converts to this ~~strike out~~.'
+        result = 'This `~~some text~~` converts to this <s>strike out</s>.'
         assert_equal(self.conv.convert(text).strip(), result)
 
     def test_convert_code_blocks(self):
-        text = u'''```python
+        text = '''```python
 print "Hello!"
 ```
 
@@ -102,7 +106,7 @@ for (var i = 0; i < a.length; i++) {
     console.log(i);
 }
 ```'''
-        result = u''':::python
+        result = ''':::python
     print "Hello!"
 
 Two code blocks here!
@@ -114,14 +118,14 @@ Two code blocks here!
         assert_equal(self.conv.convert(text).strip(), result)
 
     def test_code_blocks_without_newline_before(self):
-        text = u'''
+        text = '''
 There are some code snippet:
 ```
 print 'Hello'
 ```
 Pretty cool, ha?'''
 
-        result = u'''
+        result = '''
 There are some code snippet:
 
     print 'Hello'
@@ -130,14 +134,14 @@ Pretty cool, ha?'''
         text = text.replace('```', '~~~')
         assert_equal(self.conv.convert(text).strip(), result.strip())
 
-        text = u'''
+        text = '''
 There are some code snippet:
 ```python
 print 'Hello'
 ```
 Pretty cool, ha?'''
 
-        result = u'''
+        result = '''
 There are some code snippet:
 
     :::python