You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by ke...@apache.org on 2020/01/24 18:58:58 UTC

[allura] 05/13: [#7878] http headers must be str (not unicode in py2 or bytes in py3). Can be rolled back when in py3 exclusively I think

This is an automated email from the ASF dual-hosted git repository.

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit bd613d2fbfef980b1a78f08b31d3393fb333aee9
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Thu Jan 16 21:27:28 2020 +0000

    [#7878] http headers must be str (not unicode in py2 or bytes in py3).  Can be rolled back when in py3 exclusively I think
---
 Allura/allura/config/middleware.py                 |   4 +-
 Allura/allura/controllers/attachments.py           |   2 +-
 Allura/allura/controllers/auth.py                  |   2 +-
 Allura/allura/controllers/basetest_project_root.py |   2 +-
 Allura/allura/controllers/feed.py                  |   8 +-
 Allura/allura/controllers/project.py               |   2 +-
 Allura/allura/controllers/repository.py            |  12 +-
 Allura/allura/controllers/rest.py                  |   4 +-
 Allura/allura/controllers/root.py                  |   2 +-
 Allura/allura/controllers/task.py                  |   8 +-
 Allura/allura/lib/app_globals.py                   |   2 +-
 Allura/allura/lib/custom_middleware.py             |  22 +--
 Allura/allura/lib/decorators.py                    |   2 +-
 Allura/allura/lib/helpers.py                       |   2 +-
 Allura/allura/lib/utils.py                         |   9 +-
 Allura/allura/tests/functional/test_admin.py       |  12 +-
 Allura/allura/tests/functional/test_auth.py        | 156 +++++++--------
 Allura/allura/tests/functional/test_discuss.py     |  28 +--
 Allura/allura/tests/functional/test_feeds.py       |   3 +-
 Allura/allura/tests/functional/test_home.py        |   6 +-
 .../allura/tests/functional/test_neighborhood.py   | 210 ++++++++++-----------
 Allura/allura/tests/functional/test_rest.py        |  12 +-
 Allura/allura/tests/functional/test_root.py        |   6 +-
 Allura/allura/tests/functional/test_search.py      |   2 +-
 Allura/allura/tests/functional/test_site_admin.py  |  44 ++---
 .../allura/tests/functional/test_trovecategory.py  |   2 +-
 .../allura/tests/functional/test_user_profile.py   |   4 +-
 Allura/allura/tests/model/test_auth.py             |   2 +-
 Allura/allura/tests/model/test_filesystem.py       |   2 +-
 Allura/allura/tests/test_security.py               |   2 +-
 Allura/allura/tests/test_webhooks.py               |   6 +-
 .../allura/tests/unit/test_ldap_auth_provider.py   |   4 +-
 Allura/allura/webhooks.py                          |   2 +-
 AlluraTest/alluratest/controller.py                |   4 +-
 ForgeActivity/forgeactivity/main.py                |   6 +-
 .../forgeactivity/tests/functional/test_root.py    |   8 +-
 ForgeBlog/forgeblog/main.py                        |   6 +-
 ForgeBlog/forgeblog/tests/functional/test_rest.py  |  22 +--
 ForgeBlog/forgeblog/tests/functional/test_root.py  |  12 +-
 .../forgediscussion/tests/functional/test_forum.py |  16 +-
 .../tests/functional/test_forum_admin.py           |  14 +-
 .../forgediscussion/tests/functional/test_rest.py  |   8 +-
 .../forgegit/tests/functional/test_controllers.py  |  10 +-
 .../tests/github/functional/test_github.py         |   4 +-
 .../trac/tests/functional/test_trac.py             |   2 +-
 ForgeLink/forgelink/link_main.py                   |   2 +
 ForgeLink/forgelink/tests/functional/test_rest.py  |   8 +-
 .../forgeshorturl/tests/functional/test.py         |   6 +-
 .../forgetracker/tests/functional/test_root.py     |  72 +++----
 .../tests/unit/test_milestone_controller.py        |   2 +-
 ForgeTracker/forgetracker/tracker_main.py          |   6 +-
 ForgeWiki/forgewiki/tests/functional/test_root.py  | 171 ++++++++---------
 ForgeWiki/forgewiki/wiki_main.py                   |  14 +-
 scripts/perf/call_count.py                         |   2 +-
 54 files changed, 495 insertions(+), 486 deletions(-)

diff --git a/Allura/allura/config/middleware.py b/Allura/allura/config/middleware.py
index 1834cc6..e4f57f8 100644
--- a/Allura/allura/config/middleware.py
+++ b/Allura/allura/config/middleware.py
@@ -19,6 +19,8 @@
 
 """WSGI middleware initialization for the allura application."""
 from __future__ import unicode_literals
+
+import ast
 import importlib
 import mimetypes
 
@@ -164,7 +166,7 @@ def _make_core_app(root, global_conf, full_stack=True, **app_conf):
         use_cache=not asbool(global_conf['debug']),
         script_name=app_conf.get('ew.script_name', '/_ew_resources/'),
         url_base=app_conf.get('ew.url_base', '/_ew_resources/'),
-        extra_headers=eval(app_conf.get('ew.extra_headers', 'None')),
+        extra_headers=ast.literal_eval(app_conf.get('ew.extra_headers', '[]')),
         cache_max_age=asint(app_conf.get('ew.cache_header_seconds', 60*60*24*365)),
 
         # settings to pass through to jinja Environment for EW core widgets
diff --git a/Allura/allura/controllers/attachments.py b/Allura/allura/controllers/attachments.py
index 15419f2..9c762c7 100644
--- a/Allura/allura/controllers/attachments.py
+++ b/Allura/allura/controllers/attachments.py
@@ -49,7 +49,7 @@ class AttachmentsController(BaseController):
     def _lookup(self, filename=None, *args):
         if filename:
             if not args:
-                filename = request.path.rsplit('/', 1)[-1]
+                filename = request.path.rsplit(str('/'), 1)[-1]
             filename = unquote(filename)
             return self.AttachmentControllerClass(filename, self.artifact), args
         else:
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index b6e21c5..82ecdf3 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -392,7 +392,7 @@ class AuthController(BaseController):
             return_to = self._verify_return_to(kwargs.get('return_to'))
             redirect(return_to)
 
-    @expose(content_type='text/plain')
+    @expose(content_type=str('text/plain'))
     def refresh_repo(self, *repo_path):
         # post-commit hooks use this
         if not repo_path:
diff --git a/Allura/allura/controllers/basetest_project_root.py b/Allura/allura/controllers/basetest_project_root.py
index acdaf3e..ca2b4b6 100644
--- a/Allura/allura/controllers/basetest_project_root.py
+++ b/Allura/allura/controllers/basetest_project_root.py
@@ -112,7 +112,7 @@ class BasetestProjectRootController(WsgiDispatchController, ProjectController):
 
     def _perform_call(self, context):
         """ Called from a WebTest 'app' instance, going through TurboGears dispatcher code
-        Example: self.app.get('/auth/', extra_environ={'disable_auth_magic': "True"})
+        Example: self.app.get('/auth/', extra_environ={'disable_auth_magic': str("True")})
         """
         environ = context.request.environ
         c.app = None
diff --git a/Allura/allura/controllers/feed.py b/Allura/allura/controllers/feed.py
index b426717..5801f3a 100644
--- a/Allura/allura/controllers/feed.py
+++ b/Allura/allura/controllers/feed.py
@@ -66,8 +66,8 @@ class FeedController(object):
     a customized feed should override :meth:`get_feed`.
 
     """
-    FEED_TYPES = ['.atom', '.rss']
-    FEED_NAMES = ['feed{0}'.format(typ) for typ in FEED_TYPES]
+    FEED_TYPES = [str('.atom'), str('.rss')]
+    FEED_NAMES = [str('feed{0}'.format(typ)) for typ in FEED_TYPES]
 
     def __getattr__(self, name):
         if name in self.FEED_NAMES:
@@ -100,8 +100,8 @@ class FeedController(object):
             feed_def.url,
             feed_def.description,
             since, until, page, limit)
-        response.headers['Content-Type'] = ''
-        response.content_type = 'application/xml'
+        response.headers['Content-Type'] = str('')
+        response.content_type = str('application/xml')
         return feed.writeString('utf-8')
 
     def get_feed(self, project, app, user):
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index bdbad3c..79fd453 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -481,7 +481,7 @@ class ScreenshotsController(object):
         if args:
             filename = unquote(filename)
         else:
-            filename = unquote(request.path.rsplit('/', 1)[-1])
+            filename = unquote(request.path.rsplit(str('/'), 1)[-1])
         return ScreenshotController(filename), args
 
 
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index b4d5a33..9687741 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -800,7 +800,7 @@ class TreeBrowser(BaseController, DispatchIndex):
             # Might be a file rather than a dir
             filename = h.really_unicode(
                 unquote(
-                    request.environ['PATH_INFO'].rsplit('/')[-1]))
+                    request.environ['PATH_INFO'].rsplit(str('/'))[-1]))
             if filename:
                 try:
                     obj = self._tree[filename]
@@ -812,7 +812,7 @@ class TreeBrowser(BaseController, DispatchIndex):
                         self._tree,
                         filename), rest
         elif rest == ('index', ):
-            rest = (request.environ['PATH_INFO'].rsplit('/')[-1],)
+            rest = (request.environ['PATH_INFO'].rsplit(str('/'))[-1],)
         try:
             tree = self._tree[next]
         except KeyError:
@@ -870,15 +870,15 @@ class FileBrowser(BaseController):
     def raw(self, **kw):
         content_type = self._blob.content_type.encode('utf-8')
         filename = self._blob.name.encode('utf-8')
-        response.headers['Content-Type'] = ''
+        response.headers['Content-Type'] = str('')
         response.content_type = content_type
         if self._blob.content_encoding is not None:
             content_encoding = self._blob.content_encoding.encode('utf-8')
-            response.headers['Content-Encoding'] = ''
+            response.headers['Content-Encoding'] = str('')
             response.content_encoding = content_encoding
         response.headers.add(
-            'Content-Disposition',
-            'attachment;filename="%s"' % filename)
+            str('Content-Disposition'),
+            str('attachment;filename="%s"') % filename)
         return iter(self._blob)
 
     def diff(self, prev_commit, fmt=None, prev_file=None, **kw):
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index 8b81d99..532a073 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -391,8 +391,8 @@ class ProjectRestController(object):
     @expose('json:')
     def index(self, **kw):
         if 'doap' in kw:
-            response.headers['Content-Type'] = ''
-            response.content_type = 'application/rdf+xml'
+            response.headers['Content-Type'] = str('')
+            response.content_type = str('application/rdf+xml')
             return '<?xml version="1.0" encoding="UTF-8" ?>' + c.project.doap()
         return c.project.__json__()
 
diff --git a/Allura/allura/controllers/root.py b/Allura/allura/controllers/root.py
index 9eb4603..b8a41a6 100644
--- a/Allura/allura/controllers/root.py
+++ b/Allura/allura/controllers/root.py
@@ -113,7 +113,7 @@ class RootController(WsgiDispatchController):
             # pylons.configuration defaults to "no-cache" only.
             # See also http://blog.55minutes.com/2011/10/how-to-defeat-the-browser-back-button-cache/ and
             # https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=en#defining_optimal_cache-control_policy
-            response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
+            response.headers[str('Cache-Control')] = str('no-cache, no-store, must-revalidate')
 
     @expose()
     @with_trailing_slash
diff --git a/Allura/allura/controllers/task.py b/Allura/allura/controllers/task.py
index f7651f4..1429ac9 100644
--- a/Allura/allura/controllers/task.py
+++ b/Allura/allura/controllers/task.py
@@ -17,6 +17,10 @@
 
 
 from __future__ import unicode_literals
+
+import six
+
+
 class TaskController(object):
 
     '''WSGI app providing web-like RPC
@@ -33,6 +37,6 @@ class TaskController(object):
         nocapture = environ['nocapture']
         result = task(restore_context=False, nocapture=nocapture)
         py_response = context.response
-        py_response.headers['Content-Type'] = 'text/plain'  # `None` default is problematic for some middleware
-        py_response.body = result or ''
+        py_response.headers['Content-Type'] = str('text/plain')  # `None` default is problematic for some middleware
+        py_response.body = six.ensure_binary(result or b'')
         return py_response
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index 5f783e4..05e284f 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -354,7 +354,7 @@ class Globals(object):
             except AttributeError:
                 script_without_ming_middleware = True
             else:
-                script_without_ming_middleware = env['PATH_INFO'] == '--script--'
+                script_without_ming_middleware = env['PATH_INFO'] == str('--script--')
             if script_without_ming_middleware:
                 kwargs['flush_immediately'] = True
             else:
diff --git a/Allura/allura/lib/custom_middleware.py b/Allura/allura/lib/custom_middleware.py
index 79092fa..ab7ba60 100644
--- a/Allura/allura/lib/custom_middleware.py
+++ b/Allura/allura/lib/custom_middleware.py
@@ -76,10 +76,10 @@ class StaticFilesMiddleware(object):
                 resource_cls = ep.load().has_resource(resource_path)
                 if resource_cls:
                     file_path = pkg_resources.resource_filename(resource_cls.__module__, resource_path)
-                    return fileapp.FileApp(file_path, [('Access-Control-Allow-Origin', '*')])
+                    return fileapp.FileApp(file_path, [(str('Access-Control-Allow-Origin'), str('*'))])
         filename = environ['PATH_INFO'][len(self.script_name):]
         file_path = pkg_resources.resource_filename('allura', os.path.join('public', 'nf', filename))
-        return fileapp.FileApp(file_path, [('Access-Control-Allow-Origin', '*')])
+        return fileapp.FileApp(file_path, [(str('Access-Control-Allow-Origin'), str('*'))])
 
 
 class CORSMiddleware(object):
@@ -92,7 +92,7 @@ class CORSMiddleware(object):
         self.cache_preflight = cache or None
 
     def __call__(self, environ, start_response):
-        is_api_request = environ.get('PATH_INFO', '').startswith('/rest/')
+        is_api_request = environ.get('PATH_INFO', '').startswith(str('/rest/'))
         valid_cors = 'HTTP_ORIGIN' in environ
         if not is_api_request or not valid_cors:
             return self.app(environ, start_response)
@@ -121,17 +121,17 @@ class CORSMiddleware(object):
         return r(environ, start_response)
 
     def get_response_headers(self, preflight=False):
-        headers = [('Access-Control-Allow-Origin', '*')]
+        headers = [(str('Access-Control-Allow-Origin'), str('*'))]
         if preflight:
             ac_methods = ', '.join(self.allowed_methods)
             ac_headers = ', '.join(self.allowed_headers)
             headers.extend([
-                ('Access-Control-Allow-Methods', ac_methods),
-                ('Access-Control-Allow-Headers', ac_headers),
+                (str('Access-Control-Allow-Methods'), str(ac_methods)),
+                (str('Access-Control-Allow-Headers'), str(ac_headers)),
             ])
             if self.cache_preflight:
                 headers.append(
-                    ('Access-Control-Max-Age', str(self.cache_preflight))
+                    (str('Access-Control-Max-Age'), str(self.cache_preflight))
                 )
         return headers
 
@@ -151,7 +151,7 @@ class LoginRedirectMiddleware(object):
 
     def __call__(self, environ, start_response):
         status, headers, app_iter, exc_info = call_wsgi_application(self.app, environ)
-        is_api_request = environ.get('PATH_INFO', '').startswith('/rest/')
+        is_api_request = environ.get('PATH_INFO', '').startswith(str('/rest/'))
         if status[:3] == '401' and not is_api_request:
             login_url = tg.config.get('auth.login_url', '/auth/')
             if environ['REQUEST_METHOD'] == 'GET':
@@ -207,7 +207,7 @@ class CSRFMiddleware(object):
         def session_start_response(status, headers, exc_info=None):
             if dict(headers).get('Content-Type', '').startswith('text/html'):
                 headers.append(
-                    ('Set-cookie',
+                    (str('Set-cookie'),
                      str('%s=%s; Path=/' % (self._cookie_name, cookie))))
             return start_response(status, headers, exc_info)
 
@@ -397,12 +397,12 @@ class RememberLoginMiddleware(object):
                     session._set_cookie_expires(login_expires)
                 # Replace the cookie header that SessionMiddleware set
                 # with one that has the new expires parameter value
-                cookie = session.cookie[session.key].output(header='')
+                cookie = session.cookie[session.key].output(header=str(''))
                 for i in range(len(headers)):
                     header, contents = headers[i]
                     if header == 'Set-cookie' and \
                             contents.lstrip().startswith(session.key):
-                        headers[i] = ('Set-cookie', cookie)
+                        headers[i] = (str('Set-cookie'), cookie)
                         break
             return start_response(status, headers, exc_info)
 
diff --git a/Allura/allura/lib/decorators.py b/Allura/allura/lib/decorators.py
index eed03b7..95ab5a9 100644
--- a/Allura/allura/lib/decorators.py
+++ b/Allura/allura/lib/decorators.py
@@ -110,7 +110,7 @@ class require_post(object):
             if request.method != 'POST':
                 if self.redir is not None:
                     redirect(self.redir)
-                raise exc.HTTPMethodNotAllowed(headers={'Allow': 'POST'})
+                raise exc.HTTPMethodNotAllowed(headers={str('Allow'): str('POST')})
         before_validate(check_method)(func)
         return func
 
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index c39ee78..2ded70d 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -598,7 +598,7 @@ class fixed_attrs_proxy(proxy):
             setattr(self, k, v)
 
 
-@tg.expose(content_type='text/plain')
+@tg.expose(content_type=str('text/plain'))
 def json_validation_error(controller, **kwargs):
     exc = request.validation['exception']
     result = dict(status='Validation Error',
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index 2e6ea3b..a88e553 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -502,7 +502,7 @@ def serve_file(fp, filename, content_type, last_modified=None,
         etag = '{0}?{1}'.format(filename, last_modified).encode('utf-8')
     if etag:
         etag_cache(etag)
-    tg.response.headers['Content-Type'] = ''
+    tg.response.headers['Content-Type'] = str('')
     tg.response.content_type = content_type.encode('utf-8')
     tg.response.cache_expires = cache_expires or asint(
         tg.config.get('files_expires_header_secs', 60 * 60))
@@ -514,15 +514,16 @@ def serve_file(fp, filename, content_type, last_modified=None,
     if 'Cache-Control' in tg.response.headers:
         del tg.response.headers['Cache-Control']
     if not embed:
+        from allura.lib import helpers as h
         tg.response.headers.add(
-            'Content-Disposition',
-            'attachment;filename="%s"' % filename.encode('utf-8'))
+            str('Content-Disposition'),
+            str('attachment;filename="%s"' % h.urlquote(filename)))
     # http://code.google.com/p/modwsgi/wiki/FileWrapperExtension
     block_size = 4096
     if 'wsgi.file_wrapper' in tg.request.environ:
         return tg.request.environ['wsgi.file_wrapper'](fp, block_size)
     else:
-        return iter(lambda: fp.read(block_size), '')
+        return iter(lambda: fp.read(block_size), b'')
 
 
 class ForgeHTMLSanitizerFilter(html5lib.filters.sanitizer.Filter):
diff --git a/Allura/allura/tests/functional/test_admin.py b/Allura/allura/tests/functional/test_admin.py
index 66e6d32..6a50b1b 100644
--- a/Allura/allura/tests/functional/test_admin.py
+++ b/Allura/allura/tests/functional/test_admin.py
@@ -827,7 +827,7 @@ class TestProjectAdmin(TestController):
         # make sure can still access homepage after one of user's roles were
         # deleted
         r = self.app.get('/p/test/wiki/',
-                         extra_environ=dict(username='test-user')).follow()
+                         extra_environ=dict(username=str('test-user'))).follow()
         assert r.status == '200 OK'
 
     def test_change_perms(self):
@@ -977,17 +977,17 @@ class TestExport(TestController):
 
     def test_access(self):
         r = self.app.get('/admin/export',
-                         extra_environ={'username': '*anonymous'}).follow()
+                         extra_environ={'username': str('*anonymous')}).follow()
         assert_equals(r.request.url,
                       'http://localhost/auth/?return_to=%2Fadmin%2Fexport')
         self.app.get('/admin/export',
-                     extra_environ={'username': 'test-user'},
+                     extra_environ={'username': str('test-user')},
                      status=403)
         r = self.app.post('/admin/export',
-                          extra_environ={'username': '*anonymous'}).follow()
+                          extra_environ={'username': str('*anonymous')}).follow()
         assert_equals(r.request.url, 'http://localhost/auth/')
         self.app.post('/admin/export',
-                      extra_environ={'username': 'test-user'},
+                      extra_environ={'username': str('test-user')},
                       status=403)
 
     def test_ini_option(self):
@@ -1262,7 +1262,7 @@ class TestRestInstallTool(TestRestApiBase):
             'mount_label': 'wiki_label1'
         }
         r = self.app.post('/rest/p/test/admin/install_tool/',
-                          extra_environ={'username': '*anonymous'},
+                          extra_environ={'username': str('*anonymous')},
                           status=401,
                           params=data)
         assert_equals(r.status, '401 Unauthorized')
diff --git a/Allura/allura/tests/functional/test_auth.py b/Allura/allura/tests/functional/test_auth.py
index 653609e..4f326db 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -84,7 +84,7 @@ class TestAuth(TestController):
         r = self.app.post('/auth/do_login', antispam=True, params=dict(
             username='test-user', password='foo', honey1='robot',  # bad honeypot value
             _session_id=self.app.cookies['_session_id']),
-                          extra_environ={'regular_antispam_err_handling_even_when_tests': 'true'},
+                          extra_environ={'regular_antispam_err_handling_even_when_tests': str('true')},
                           status=302)
         wf = json.loads(self.webflash(r))
         assert_equal(wf['status'], 'error')
@@ -102,18 +102,18 @@ class TestAuth(TestController):
         assert 'Invalid login' in str(r), r.showbrowser()
 
     def test_login_invalid_username(self):
-        extra = {'username': '*anonymous'}
+        extra = {'username': str('*anonymous')}
         r = self.app.get('/auth/', extra_environ=extra)
         f = r.forms[0]
         encoded = self.app.antispam_field_names(f)
         f[encoded['username']] = 'test@user.com'
         f[encoded['password']] = 'foo'
-        r = f.submit(extra_environ={'username': '*anonymous'})
+        r = f.submit(extra_environ={'username': str('*anonymous')})
         r.mustcontain('Usernames only include small letters, ')
 
     def test_login_diff_ips_ok(self):
         # exercises AntiSpam.validate methods
-        extra = {'username': '*anonymous', 'REMOTE_ADDR': '11.22.33.44'}
+        extra = {'username': str('*anonymous'), 'REMOTE_ADDR': str('11.22.33.44')}
         r = self.app.get('/auth/', extra_environ=extra)
 
         f = r.forms[0]
@@ -121,19 +121,19 @@ class TestAuth(TestController):
         f[encoded['username']] = 'test-user'
         f[encoded['password']] = 'foo'
         with audits('Successful login', user=True):
-            r = f.submit(extra_environ={'username': '*anonymous', 'REMOTE_ADDR': '11.22.33.99'})
+            r = f.submit(extra_environ={'username': str('*anonymous'), 'REMOTE_ADDR': str('11.22.33.99')})
 
     def test_login_diff_ips_bad(self):
         # exercises AntiSpam.validate methods
-        extra = {'username': '*anonymous', 'REMOTE_ADDR': '24.52.32.123'}
+        extra = {'username': str('*anonymous'), 'REMOTE_ADDR': str('24.52.32.123')}
         r = self.app.get('/auth/', extra_environ=extra)
 
         f = r.forms[0]
         encoded = self.app.antispam_field_names(f)
         f[encoded['username']] = 'test-user'
         f[encoded['password']] = 'foo'
-        r = f.submit(extra_environ={'username': '*anonymous', 'REMOTE_ADDR': '11.22.33.99',
-                                    'regular_antispam_err_handling_even_when_tests': 'true'},
+        r = f.submit(extra_environ={'username': str('*anonymous'), 'REMOTE_ADDR': str('11.22.33.99'),
+                                    'regular_antispam_err_handling_even_when_tests': str('true')},
                      status=302)
         wf = json.loads(self.webflash(r))
         assert_equal(wf['status'], 'error')
@@ -143,7 +143,7 @@ class TestAuth(TestController):
     @patch('allura.tasks.mail_tasks.sendsimplemail')
     def test_login_hibp_compromised_password_untrusted_client(self, sendsimplemail):
         # first & only login by this user, so won't have any trusted previous logins
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
         r = self.app.get('/auth/')
         f = r.forms[0]
         encoded = self.app.antispam_field_names(f)
@@ -165,7 +165,7 @@ class TestAuth(TestController):
 
     @patch('allura.tasks.mail_tasks.sendsimplemail')
     def test_login_hibp_compromised_password_trusted_client(self, sendsimplemail):
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
 
         # regular login first, so IP address will be recorded and then trusted
         r = self.app.get('/auth/')
@@ -201,43 +201,43 @@ class TestAuth(TestController):
     def test_login_disabled(self):
         u = M.User.query.get(username='test-user')
         u.disabled = True
-        r = self.app.get('/auth/', extra_environ={'username': '*anonymous'})
+        r = self.app.get('/auth/', extra_environ={'username': str('*anonymous')})
         f = r.forms[0]
         encoded = self.app.antispam_field_names(f)
         f[encoded['username']] = 'test-user'
         f[encoded['password']] = 'foo'
         with audits('Failed login', user=True):
-            r = f.submit(extra_environ={'username': '*anonymous'})
+            r = f.submit(extra_environ={'username': str('*anonymous')})
 
     def test_login_pending(self):
         u = M.User.query.get(username='test-user')
         u.pending = True
-        r = self.app.get('/auth/', extra_environ={'username': '*anonymous'})
+        r = self.app.get('/auth/', extra_environ={'username': str('*anonymous')})
         f = r.forms[0]
         encoded = self.app.antispam_field_names(f)
         f[encoded['username']] = 'test-user'
         f[encoded['password']] = 'foo'
         with audits('Failed login', user=True):
-            r = f.submit(extra_environ={'username': '*anonymous'})
+            r = f.submit(extra_environ={'username': str('*anonymous')})
 
     def test_login_overlay(self):
-        r = self.app.get('/auth/login_fragment/', extra_environ={'username': '*anonymous'})
+        r = self.app.get('/auth/login_fragment/', extra_environ={'username': str('*anonymous')})
         f = r.forms[0]
         encoded = self.app.antispam_field_names(f)
         f[encoded['username']] = 'test-user'
         f[encoded['password']] = 'foo'
         with audits('Successful login', user=True):
-            r = f.submit(extra_environ={'username': '*anonymous'})
+            r = f.submit(extra_environ={'username': str('*anonymous')})
 
     def test_logout(self):
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
         nav_pattern = ('nav', {'class': 'nav-main'})
         r = self.app.get('/auth/')
 
         r = self.app.post('/auth/do_login', params=dict(
             username='test-user', password='foo',
             _session_id=self.app.cookies['_session_id']),
-            extra_environ={'REMOTE_ADDR': '127.0.0.1'},
+            extra_environ={'REMOTE_ADDR': str('127.0.0.1')},
             antispam=True).follow().follow()
 
         logged_in_session = r.session['_id']
@@ -258,8 +258,8 @@ class TestAuth(TestController):
 
         self.app.get('/').follow()  # establish session
         self.app.post('/auth/do_login',
-                      headers={'User-Agent': 'browser'},
-                      extra_environ={'REMOTE_ADDR': '127.0.0.1'},
+                      headers={str('User-Agent'): str('browser')},
+                      extra_environ={'REMOTE_ADDR': str('127.0.0.1')},
                       params=dict(
                           username='test-user',
                           password='foo',
@@ -316,7 +316,7 @@ class TestAuth(TestController):
                           'password': 'foo',
                           '_session_id': self.app.cookies['_session_id'],
                       },
-                      extra_environ=dict(username='test-admin'))
+                      extra_environ=dict(username=str('test-admin')))
 
         assert M.EmailAddress.find(dict(email=email_address, claimed_by_user_id=user._id)).count() == 1
         r = self.app.post('/auth/preferences/update_emails',
@@ -328,7 +328,7 @@ class TestAuth(TestController):
                               'password': 'foo',
                               '_session_id': self.app.cookies['_session_id'],
                           },
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
 
         assert json.loads(self.webflash(r))['status'] == 'error', self.webflash(r)
         assert M.EmailAddress.find(dict(email=email_address, claimed_by_user_id=user._id)).count() == 1
@@ -362,7 +362,7 @@ class TestAuth(TestController):
                               'password': 'foo',
                               '_session_id': self.app.cookies['_session_id'],
                           },
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
 
         assert json.loads(self.webflash(r))['status'] == 'ok'
         assert json.loads(self.webflash(r))['message'] == 'A verification email has been sent.  ' \
@@ -406,7 +406,7 @@ class TestAuth(TestController):
                               'password': 'foo',
                               '_session_id': self.app.cookies['_session_id'],
                           },
-                          extra_environ=dict(username='test-user-1'))
+                          extra_environ=dict(username=str('test-user-1')))
 
         assert json.loads(self.webflash(r))['status'] == 'ok'
         assert json.loads(self.webflash(r))['message'] == 'A verification email has been sent.  ' \
@@ -430,7 +430,7 @@ class TestAuth(TestController):
                                   'password': 'foo',
                                   '_session_id': self.app.cookies['_session_id'],
                               },
-                              extra_environ=dict(username='test-user-1'))
+                              extra_environ=dict(username=str('test-user-1')))
             assert json.loads(self.webflash(r))['status'] == 'ok'
 
             r = self.app.post('/auth/preferences/update_emails',
@@ -442,7 +442,7 @@ class TestAuth(TestController):
                                   'password': 'foo',
                                   '_session_id': self.app.cookies['_session_id'],
                               },
-                              extra_environ=dict(username='test-user-1'))
+                              extra_environ=dict(username=str('test-user-1')))
 
             assert json.loads(self.webflash(r))['status'] == 'error'
             assert json.loads(self.webflash(r))['message'] == 'You cannot claim more than 2 email addresses.'
@@ -468,7 +468,7 @@ class TestAuth(TestController):
 
         r = self.app.post('/auth/send_verification_link',
                           params=dict(a=email_address, _session_id=self.app.cookies['_session_id']),
-                          extra_environ=dict(username='test-user-1', _session_id=self.app.cookies['_session_id']))
+                          extra_environ=dict(username=str('test-user-1'), _session_id=self.app.cookies['_session_id']))
 
         assert json.loads(self.webflash(r))['status'] == 'ok'
         assert json.loads(self.webflash(r))['message'] == 'Verification link sent'
@@ -494,7 +494,7 @@ class TestAuth(TestController):
         self.app.post('/auth/send_verification_link',
                       params=dict(a=email_address,
                                   _session_id=self.app.cookies['_session_id']),
-                      extra_environ=dict(username='test-user'))
+                      extra_environ=dict(username=str('test-user')))
 
         user1 = M.User.query.get(username='test-user-1')
         user1.claim_address(email_address)
@@ -502,7 +502,7 @@ class TestAuth(TestController):
         email1.confirmed = True
         ThreadLocalORMSession.flush_all()
         # Verify first email with the verification link
-        r = self.app.get('/auth/verify_addr', params=dict(a=email.nonce), extra_environ=dict(username='test-user'))
+        r = self.app.get('/auth/verify_addr', params=dict(a=email.nonce), extra_environ=dict(username=str('test-user')))
 
         assert json.loads(self.webflash(r))['status'] == 'error'
         email = M.EmailAddress.find(dict(email=email_address, claimed_by_user_id=user._id)).first()
@@ -522,20 +522,20 @@ class TestAuth(TestController):
         self.app.post('/auth/send_verification_link',
                       params=dict(a=email_address,
                                   _session_id=self.app.cookies['_session_id']),
-                      extra_environ=dict(username='test-user'))
+                      extra_environ=dict(username=str('test-user')))
 
         # logged out, gets redirected to login page
-        r = self.app.get('/auth/verify_addr', params=dict(a=email.nonce), extra_environ=dict(username='*anonymous'))
+        r = self.app.get('/auth/verify_addr', params=dict(a=email.nonce), extra_environ=dict(username=str('*anonymous')))
         assert_in('/auth/?return_to=%2Fauth%2Fverify_addr', r.location)
 
         # logged in as someone else
-        r = self.app.get('/auth/verify_addr', params=dict(a=email.nonce), extra_environ=dict(username='test-admin'))
+        r = self.app.get('/auth/verify_addr', params=dict(a=email.nonce), extra_environ=dict(username=str('test-admin')))
         assert_in('/auth/?return_to=%2Fauth%2Fverify_addr', r.location)
         assert_equal('You must be logged in to the correct account', json.loads(self.webflash(r))['message'])
         assert_equal('warning', json.loads(self.webflash(r))['status'])
 
         # logged in as correct user
-        r = self.app.get('/auth/verify_addr', params=dict(a=email.nonce), extra_environ=dict(username='test-user'))
+        r = self.app.get('/auth/verify_addr', params=dict(a=email.nonce), extra_environ=dict(username=str('test-user')))
         assert_in('confirmed', json.loads(self.webflash(r))['message'])
         assert_equal('ok', json.loads(self.webflash(r))['status'])
 
@@ -595,7 +595,7 @@ class TestAuth(TestController):
         self.app.get('/').follow()  # establish session
         change_params['_session_id'] = self.app.cookies['_session_id']
         self.app.post('/auth/preferences/update_emails',
-                      extra_environ=dict(username='test-admin'),
+                      extra_environ=dict(username=str('test-admin')),
                       params=change_params)
 
         u = M.User.by_username('test-admin')
@@ -613,7 +613,7 @@ class TestAuth(TestController):
         # Change password
         with audits('Password changed', user=True):
             self.app.post('/auth/preferences/change_password',
-                          extra_environ=dict(username='test-admin'),
+                          extra_environ=dict(username=str('test-admin')),
                           params={
                               'oldpw': 'foo',
                               'pw': 'asdfasdf',
@@ -644,7 +644,7 @@ class TestAuth(TestController):
 
         # Attempt change password with weak pwd
         r = self.app.post('/auth/preferences/change_password',
-                      extra_environ=dict(username='test-admin'),
+                      extra_environ=dict(username=str('test-admin')),
                       params={
                           'oldpw': 'foo',
                           'pw': 'password',
@@ -655,7 +655,7 @@ class TestAuth(TestController):
         assert 'Unsafe' in str(r.headers)
 
         r = self.app.post('/auth/preferences/change_password',
-                          extra_environ=dict(username='test-admin'),
+                          extra_environ=dict(username=str('test-admin')),
                           params={
                               'oldpw': 'foo',
                               'pw': '3j84rhoirwnoiwrnoiw',
@@ -671,7 +671,7 @@ class TestAuth(TestController):
     @td.with_user_project('test-admin')
     def test_prefs(self):
         r = self.app.get('/auth/preferences/',
-                         extra_environ=dict(username='test-admin'))
+                         extra_environ=dict(username=str('test-admin')))
         # check preconditions of test data
         assert 'test@example.com' not in r
         assert 'test-admin@users.localhost' in r
@@ -681,7 +681,7 @@ class TestAuth(TestController):
         # add test@example
         with td.audits('New email address: test@example.com', user=True):
             r = self.app.post('/auth/preferences/update_emails',
-                              extra_environ=dict(username='test-admin'),
+                              extra_environ=dict(username=str('test-admin')),
                               params={
                                   'new_addr.addr': 'test@example.com',
                                   'new_addr.claim': 'Claim Address',
@@ -698,7 +698,7 @@ class TestAuth(TestController):
         # remove test-admin@users.localhost
         with td.audits('Email address deleted: test-admin@users.localhost', user=True):
             r = self.app.post('/auth/preferences/update_emails',
-                              extra_environ=dict(username='test-admin'),
+                              extra_environ=dict(username=str('test-admin')),
                               params={
                                   'addr-1.ord': '1',
                                   'addr-1.delete': 'on',
@@ -720,7 +720,7 @@ class TestAuth(TestController):
                               params={'preferences.display_name': 'Admin',
                                       '_session_id': self.app.cookies['_session_id'],
                                       },
-                              extra_environ=dict(username='test-admin'))
+                              extra_environ=dict(username=str('test-admin')))
 
     @td.with_user_project('test-admin')
     def test_email_prefs_change_requires_password(self):
@@ -734,21 +734,21 @@ class TestAuth(TestController):
         }
         r = self.app.post('/auth/preferences/update_emails',
                           params=new_email_params,
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
         assert_in('You must provide your current password to claim new email', self.webflash(r))
         assert_not_in('test@example.com', r.follow())
         new_email_params['password'] = 'bad pass'
 
         r = self.app.post('/auth/preferences/update_emails',
                           params=new_email_params,
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
         assert_in('You must provide your current password to claim new email', self.webflash(r))
         assert_not_in('test@example.com', r.follow())
         new_email_params['password'] = 'foo'  # valid password
 
         r = self.app.post('/auth/preferences/update_emails',
                           params=new_email_params,
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
         assert_not_in('You must provide your current password to claim new email', self.webflash(r))
         assert_in('test@example.com', r.follow())
 
@@ -760,14 +760,14 @@ class TestAuth(TestController):
         }
         r = self.app.post('/auth/preferences/update_emails',
                           params=change_primary_params,
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
         assert_in('You must provide your current password to change primary address', self.webflash(r))
         assert_equal(M.User.by_username('test-admin').get_pref('email_address'), 'test-admin@users.localhost')
         change_primary_params['password'] = 'bad pass'
 
         r = self.app.post('/auth/preferences/update_emails',
                           params=change_primary_params,
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
         assert_in('You must provide your current password to change primary address', self.webflash(r))
         assert_equal(M.User.by_username('test-admin').get_pref('email_address'), 'test-admin@users.localhost')
         change_primary_params['password'] = 'foo'  # valid password
@@ -775,7 +775,7 @@ class TestAuth(TestController):
         self.app.get('/auth/preferences/')  # let previous 'flash' message cookie get used up
         r = self.app.post('/auth/preferences/update_emails',
                           params=change_primary_params,
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
         assert_not_in('You must provide your current password to change primary address', self.webflash(r))
         assert_equal(M.User.by_username('test-admin').get_pref('email_address'), 'test@example.com')
 
@@ -790,26 +790,26 @@ class TestAuth(TestController):
         }
         r = self.app.post('/auth/preferences/update_emails',
                           params=remove_email_params,
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
         assert_in('You must provide your current password to delete an email', self.webflash(r))
         assert_in('test@example.com', r.follow())
         remove_email_params['password'] = 'bad pass'
         r = self.app.post('/auth/preferences/update_emails',
                           params=remove_email_params,
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
         assert_in('You must provide your current password to delete an email', self.webflash(r))
         assert_in('test@example.com', r.follow())
         remove_email_params['password'] = 'foo'  # vallid password
         r = self.app.post('/auth/preferences/update_emails',
                           params=remove_email_params,
-                          extra_environ=dict(username='test-admin'))
+                          extra_environ=dict(username=str('test-admin')))
         assert_not_in('You must provide your current password to delete an email', self.webflash(r))
         assert_not_in('test@example.com', r.follow())
 
     @td.with_user_project('test-admin')
     def test_prefs_subscriptions(self):
         r = self.app.get('/auth/subscriptions/',
-                         extra_environ=dict(username='test-admin'))
+                         extra_environ=dict(username=str('test-admin')))
         subscriptions = M.Mailbox.query.find(dict(
             user_id=c.user._id, is_flash=False)).all()
         # make sure page actually lists all the user's subscriptions
@@ -868,7 +868,7 @@ class TestAuth(TestController):
     @td.with_user_project('test-admin')
     def test_prefs_subscriptions_subscribe(self):
         resp = self.app.get('/auth/subscriptions/',
-                            extra_environ=dict(username='test-admin'))
+                            extra_environ=dict(username=str('test-admin')))
         form = self._find_subscriptions_form(resp)
         # find not subscribed tool, subscribe and verify
         field_name = self._find_subscriptions_field(form, subscribed=False)
@@ -884,7 +884,7 @@ class TestAuth(TestController):
     @td.with_user_project('test-admin')
     def test_prefs_subscriptions_unsubscribe(self):
         resp = self.app.get('/auth/subscriptions/',
-                            extra_environ=dict(username='test-admin'))
+                            extra_environ=dict(username=str('test-admin')))
         form = self._find_subscriptions_form(resp)
         field_name = self._find_subscriptions_field(form, subscribed=True)
         s_id = ObjectId(form.fields[field_name + '.subscription_id'][0].value)
@@ -1047,7 +1047,7 @@ class TestAuth(TestController):
             dict(user_id=user._id, project_id=p._id)).count() == 0
 
         self.app.get('/p/test/admin/permissions',
-                     extra_environ=dict(username='aaa'), status=403)
+                     extra_environ=dict(username=str('aaa')), status=403)
         assert M.ProjectRole.query.find(
             dict(user_id=user._id, project_id=p._id)).count() <= 1
 
@@ -1060,7 +1060,7 @@ class TestAuth(TestController):
         sess = session(user)
         assert not user.disabled
         r = self.app.get('/p/test/admin/',
-                         extra_environ={'username': 'test-admin'})
+                         extra_environ={'username': str('test-admin')})
         assert_equal(r.status_int, 200, 'Redirect to %s' % r.location)
         user.disabled = True
         sess.save(user)
@@ -1068,7 +1068,7 @@ class TestAuth(TestController):
         user = M.User.query.get(username='test-admin')
         assert user.disabled
         r = self.app.get('/p/test/admin/',
-                         extra_environ={'username': 'test-admin'})
+                         extra_environ={'username': str('test-admin')})
         assert_equal(r.status_int, 302)
         assert_equal(r.location, 'http://localhost/auth/?return_to=%2Fp%2Ftest%2Fadmin%2F')
 
@@ -1509,7 +1509,7 @@ class TestPasswordReset(TestController):
     def setUp(self):
         super(TestPasswordReset, self).setUp()
         # so test-admin isn't automatically logged in for all requests
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
 
     @patch('allura.tasks.mail_tasks.sendmail')
     @patch('allura.lib.helpers.gen_message_id')
@@ -1768,7 +1768,7 @@ class TestOAuth(TestController):
                                   }, status=302)
         r = self.app.get('/auth/oauth/')
         assert_equal(r.forms[1].action, 'generate_access_token')
-        r = r.forms[1].submit(extra_environ={'username': 'test-user'})  # not the right user
+        r = r.forms[1].submit(extra_environ={'username': str('test-user')})  # not the right user
         assert_in("Invalid app ID", self.webflash(r))                   # gets an error
         r = self.app.get('/auth/oauth/')                                # do it again
         r = r.forms[1].submit()                                         # as correct user
@@ -2083,7 +2083,7 @@ class TestDisableAccount(TestController):
     def test_not_authenticated(self):
         r = self.app.get(
             '/auth/disable/',
-            extra_environ={'username': '*anonymous'})
+            extra_environ={'username': str('*anonymous')})
         assert_equal(r.status_int, 302)
         assert_equal(r.location,
                      'http://localhost/auth/?return_to=%2Fauth%2Fdisable%2F')
@@ -2125,21 +2125,21 @@ class TestDisableAccount(TestController):
 
 class TestPasswordExpire(TestController):
     def login(self, username='test-user', pwd='foo', query_string=''):
-        extra = extra_environ={'username': '*anonymous', 'REMOTE_ADDR':'127.0.0.1'}
+        extra = extra_environ={'username': str('*anonymous'), 'REMOTE_ADDR': str('127.0.0.1')}
         r = self.app.get('/auth/' + query_string, extra_environ=extra)
 
         f = r.forms[0]
         encoded = self.app.antispam_field_names(f)
         f[encoded['username']] = username
         f[encoded['password']] = pwd
-        return f.submit(extra_environ={'username': '*anonymous'})
+        return f.submit(extra_environ={'username': str('*anonymous')})
 
     def assert_redirects(self, where='/'):
-        resp = self.app.get(where, extra_environ={'username': 'test-user'}, status=302)
+        resp = self.app.get(where, extra_environ={'username': str('test-user')}, status=302)
         assert_equal(resp.location, 'http://localhost/auth/pwd_expired?' + urlencode({'return_to': where}))
 
     def assert_not_redirects(self, where='/neighborhood'):
-        self.app.get(where, extra_environ={'username': 'test-user'}, status=200)
+        self.app.get(where, extra_environ={'username': str('test-user')}, status=200)
 
     def test_disabled(self):
         r = self.login()
@@ -2191,7 +2191,7 @@ class TestPasswordExpire(TestController):
             r = self.login()
             assert_true(self.expired(r))
             self.assert_redirects()
-            r = self.app.get('/auth/logout', extra_environ={'username': 'test-user'})
+            r = self.app.get('/auth/logout', extra_environ={'username': str('test-user')})
             assert_false(self.expired(r))
             self.assert_not_redirects()
 
@@ -2205,12 +2205,12 @@ class TestPasswordExpire(TestController):
             user = M.User.by_username('test-user')
             old_update_time = user.last_password_updated
             old_password = user.password
-            r = self.app.get('/auth/pwd_expired', extra_environ={'username': 'test-user'})
+            r = self.app.get('/auth/pwd_expired', extra_environ={'username': str('test-user')})
             f = r.forms[0]
             f['oldpw'] = 'foo'
             f['pw'] = 'qwerty'
             f['pw2'] = 'qwerty'
-            r = f.submit(extra_environ={'username': 'test-user'}, status=302)
+            r = f.submit(extra_environ={'username': str('test-user')}, status=302)
             assert_equal(r.location, 'http://localhost/')
             assert_false(self.expired(r))
             user = M.User.by_username('test-user')
@@ -2245,12 +2245,12 @@ class TestPasswordExpire(TestController):
             session(user).flush(user)
 
             # Change expired password
-            r = self.app.get('/auth/pwd_expired', extra_environ={'username': 'test-user'})
+            r = self.app.get('/auth/pwd_expired', extra_environ={'username': str('test-user')})
             f = r.forms[0]
             f['oldpw'] = 'foo'
             f['pw'] = 'qwerty'
             f['pw2'] = 'qwerty'
-            r = f.submit(extra_environ={'username': 'test-user'}, status=302)
+            r = f.submit(extra_environ={'username': str('test-user')}, status=302)
             assert_equal(r.location, 'http://localhost/')
 
             user = M.User.by_username('test-user')
@@ -2264,12 +2264,12 @@ class TestPasswordExpire(TestController):
         user = M.User.by_username('test-user')
         old_update_time = user.last_password_updated
         old_password = user.password
-        r = self.app.get('/auth/pwd_expired', extra_environ={'username': 'test-user'})
+        r = self.app.get('/auth/pwd_expired', extra_environ={'username': str('test-user')})
         f = r.forms[0]
         f['oldpw'] = oldpw
         f['pw'] = pw
         f['pw2'] = pw2
-        r = f.submit(extra_environ={'username': 'test-user'})
+        r = f.submit(extra_environ={'username': str('test-user')})
         assert_true(self.expired(r))
         user = M.User.by_username('test-user')
         assert_equal(user.last_password_updated, old_update_time)
@@ -2308,20 +2308,20 @@ class TestPasswordExpire(TestController):
             # but if user tries to go directly there anyway, intercept and redirect back
             self.assert_redirects(where=return_to)
 
-            r = self.app.get('/auth/pwd_expired', extra_environ={'username': 'test-user'})
+            r = self.app.get('/auth/pwd_expired', extra_environ={'username': str('test-user')})
             f = r.forms[0]
             f['oldpw'] = 'foo'
             f['pw'] = 'qwerty'
             f['pw2'] = 'qwerty'
             f['return_to'] = return_to
-            r = f.submit(extra_environ={'username': 'test-user'}, status=302)
+            r = f.submit(extra_environ={'username': str('test-user')}, status=302)
             assert_equal(r.location, 'http://localhost/p/test/tickets/?milestone=1.0&page=2')
 
 
 class TestCSRFProtection(TestController):
     def test_blocks_invalid(self):
         # so test-admin isn't automatically logged in for all requests
-        self.app.extra_environ = {'disable_auth_magic': 'True', 'REMOTE_ADDR': '127.0.0.1'}
+        self.app.extra_environ = {'disable_auth_magic': str('True'), 'REMOTE_ADDR': str('127.0.0.1')}
 
         # regular login
         r = self.app.get('/auth/')
@@ -2545,7 +2545,7 @@ class TestTwoFactor(TestController):
         self._init_totp()
 
         # so test-admin isn't automatically logged in for all requests
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
 
         # regular login
         r = self.app.get('/auth/?return_to=/p/foo')
@@ -2582,7 +2582,7 @@ class TestTwoFactor(TestController):
         self._init_totp()
 
         # so test-admin isn't automatically logged in for all requests
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
 
         # regular login
         r = self.app.get('/auth/?return_to=/p/foo')
@@ -2613,7 +2613,7 @@ class TestTwoFactor(TestController):
         self._init_totp()
 
         # so test-admin isn't automatically logged in for all requests
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
 
         # regular login
         r = self.app.get('/auth/')
@@ -2642,7 +2642,7 @@ class TestTwoFactor(TestController):
         self._init_totp()
 
         # so test-admin isn't automatically logged in for all requests
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
 
         # regular login
         r = self.app.get('/auth/?return_to=/p/foo')
@@ -2689,7 +2689,7 @@ class TestTwoFactor(TestController):
         self._init_totp()
 
         # so test-admin isn't automatically logged in for all requests
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
 
         # regular login
         r = self.app.get('/auth/?return_to=/p/foo')
diff --git a/Allura/allura/tests/functional/test_discuss.py b/Allura/allura/tests/functional/test_discuss.py
index e94bf53..4a87fd1 100644
--- a/Allura/allura/tests/functional/test_discuss.py
+++ b/Allura/allura/tests/functional/test_discuss.py
@@ -81,8 +81,8 @@ class TestDiscuss(TestDiscussBase):
                 params[field['name']] = field.get('value') or ''
         params[f.find('textarea')['name']] = text
         r = self.app.post(f['action'].encode('utf-8'), params=params,
-                          headers={'Referer': thread_link.encode("utf-8")},
-                          extra_environ=dict(username='root'))
+                          headers={str('Referer'): str(thread_link.encode("utf-8"))},
+                          extra_environ=dict(username=str('root')))
         r = r.follow()
         return r
 
@@ -107,7 +107,7 @@ class TestDiscuss(TestDiscussBase):
         params[post_form.find('textarea')['name']] = 'This is a new post'
         r = self.app.post(post_link,
                           params=params,
-                          headers={'Referer': thread_link.encode("utf-8")})
+                          headers={str('Referer'): str(thread_link.encode("utf-8"))})
         r = r.follow()
         assert 'This is a new post' in r, r
         r = self.app.get(post_link)
@@ -121,7 +121,7 @@ class TestDiscuss(TestDiscussBase):
         params[post_form.find('textarea')['name']] = 'Tis a reply'
         r = self.app.post(post_link + 'reply',
                           params=params,
-                          headers={'Referer': post_link.encode("utf-8")})
+                          headers={str('Referer'): str(post_link.encode("utf-8"))})
         r = self.app.get(thread_link)
         assert 'Tis a reply' in r, r
         permalinks = [post.find('form')['action'].encode('utf-8')
@@ -149,7 +149,7 @@ class TestDiscuss(TestDiscussBase):
         # ok initially
         non_admin = 'test-user'
         self.app.get(thread_url, status=200,
-                     extra_environ=dict(username=non_admin))
+                     extra_environ=dict(username=str(non_admin)))
 
         # set wiki page private
         from forgewiki.model import Page
@@ -163,14 +163,14 @@ class TestDiscuss(TestDiscussBase):
         ]
 
         self.app.get(thread_url, status=200,  # ok
-                     extra_environ=dict(username='test-admin'))
+                     extra_environ=dict(username=str('test-admin')))
         self.app.get(thread_url, status=403,  # forbidden
-                     extra_environ=dict(username=non_admin))
+                     extra_environ=dict(username=str(non_admin)))
 
     def test_spam_link(self):
         r = self._make_post('Test post')
         assert '<span><i class="fa fa-exclamation" aria-hidden="true"></i></span>' in r
-        r = self.app.get('/wiki/Home/', extra_environ={'username': 'test-user-1'})
+        r = self.app.get('/wiki/Home/', extra_environ={'username': str('test-user-1')})
         assert '<span><i class="fa fa-exclamation" aria-hidden="true"></i></span>' not in r, 'User without moderate perm must not see Spam link'
 
     @patch('allura.controllers.discuss.g.spam_checker.submit_spam')
@@ -214,7 +214,7 @@ class TestDiscuss(TestDiscussBase):
             update_link,
             params={
                 'text': '- [x] checkbox'},
-            extra_environ=dict(username='*anonymous'))
+            extra_environ=dict(username=str('*anonymous')))
         assert response.json['status'] == 'no_permission'
 
     def test_comment_post_reaction_new(self):
@@ -239,14 +239,14 @@ class TestDiscuss(TestDiscussBase):
             react_link,
             params={
                 'r': ':+1:'},
-            extra_environ=dict(username='*anonymous'))
+            extra_environ=dict(username=str('*anonymous')))
         assert response.json['error'] == 'no_permission'
         # even anon can't send invalid reactions
         response = self.app.post(
             react_link,
             params={
                 'r': 'invalid'},
-            extra_environ=dict(username='*anonymous'))
+            extra_environ=dict(username=str('*anonymous')))
         assert response.json['error'] == 'no_permission'
 
     def test_comment_post_reaction_change(self):
@@ -417,7 +417,7 @@ class TestAttachment(TestDiscussBase):
                 params[field['name']] = field.get('value') or ''
         params[f.find('textarea')['name']] = 'Test Post'
         r = self.app.post(f['action'].encode('utf-8'), params=params,
-                          headers={'Referer': self.thread_link.encode('utf-8')})
+                          headers={str('Referer'): str(self.thread_link.encode('utf-8'))})
         r = r.follow()
         self.post_link = str(
             r.html.find('div', {'class': 'edit_post_form reply'}).find('form')['action'])
@@ -500,8 +500,8 @@ class TestAttachment(TestDiscussBase):
         self.app.get(thumblink, status=404)
 
     def test_unmoderated_post_attachments(self):
-        ordinary_user = {'username': 'test-user'}
-        moderator = {'username': 'test-admin'}
+        ordinary_user = {'username': str('test-user')}
+        moderator = {'username': str('test-admin')}
         # set up attachment
         f = os.path.join(os.path.dirname(__file__), '..', 'data', 'user.png')
         with open(f) as f:
diff --git a/Allura/allura/tests/functional/test_feeds.py b/Allura/allura/tests/functional/test_feeds.py
index 7558db9..863e9a2 100644
--- a/Allura/allura/tests/functional/test_feeds.py
+++ b/Allura/allura/tests/functional/test_feeds.py
@@ -20,6 +20,7 @@ from formencode.variabledecode import variable_encode
 
 from allura.tests import TestController
 from allura.tests import decorators as td
+from allura.lib import helpers as h
 
 
 class TestFeeds(TestController):
@@ -91,7 +92,7 @@ class TestFeeds(TestController):
             summary='This is a new ticket',
             status='unread',
             milestone='',
-            description='This is another description'), extra_environ=dict(username='root'))
+            description='This is another description'), extra_environ=dict(username=str('root')))
         r = self.app.get('/bugs/1/feed.atom')
         assert '=&amp;gt' in r
         assert '\n+' in r
diff --git a/Allura/allura/tests/functional/test_home.py b/Allura/allura/tests/functional/test_home.py
index 4ab8147..8a5beb9 100644
--- a/Allura/allura/tests/functional/test_home.py
+++ b/Allura/allura/tests/functional/test_home.py
@@ -222,13 +222,13 @@ class TestProjectHome(TestController):
 
     def test_members_anonymous(self):
         r = self.app.get('/p/test/_members/',
-                         extra_environ=dict(username='*anonymous'))
+                         extra_environ=dict(username=str('*anonymous')))
         assert '<td>Test Admin</td>' in r
         assert '<td><a href="/u/test-admin/">test-admin</a></td>' in r
         assert '<td>Admin</td>' in r
 
     def test_toolaccess_before_subproject(self):
-        self.app.extra_environ = {'username': 'test-admin'}
+        self.app.extra_environ = {'username': str('test-admin')}
         # Add the subproject with a wiki.
         self.app.post('/p/test/admin/update_mounts', params={
             'new.install': 'install',
@@ -259,7 +259,7 @@ class TestProjectHome(TestController):
         })
 
         # Try to access the  installed tool as anon.
-        r = self.app.get('/p/test/test-mount/test-sub/', extra_environ=dict(username='*anonymous'), status=404)
+        r = self.app.get('/p/test/test-mount/test-sub/', extra_environ=dict(username=str('*anonymous')), status=404)
 
         # Try to access the installed tool as Admin.
         r = self.app.get('/p/test/test-mount/test-sub/').follow()
diff --git a/Allura/allura/tests/functional/test_neighborhood.py b/Allura/allura/tests/functional/test_neighborhood.py
index 2e7d65a..11e5074 100644
--- a/Allura/allura/tests/functional/test_neighborhood.py
+++ b/Allura/allura/tests/functional/test_neighborhood.py
@@ -46,13 +46,13 @@ class TestNeighborhood(TestController):
         r = r.follow()
         assert 'This is the "Adobe" neighborhood' in str(r), str(r)
         r = self.app.get(
-            '/adobe/admin/', extra_environ=dict(username='test-user'),
+            '/adobe/admin/', extra_environ=dict(username=str('test-user')),
             status=403)
 
     def test_redirect(self):
         r = self.app.post('/adobe/_admin/update',
                           params=dict(redirect='wiki/Home/'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         r = self.app.get('/adobe/')
         assert r.location.endswith('/adobe/wiki/Home/')
 
@@ -62,25 +62,25 @@ class TestNeighborhood(TestController):
         assert 'This is the "Adobe" neighborhood' in str(r), str(r)
 
     def test_admin(self):
-        r = self.app.get('/adobe/_admin/', extra_environ=dict(username='root'))
+        r = self.app.get('/adobe/_admin/', extra_environ=dict(username=str('root')))
         r = self.app.get('/adobe/_admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         r = self.app.get('/adobe/_admin/accolades',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         neighborhood = M.Neighborhood.query.get(name='Adobe')
         neighborhood.features['google_analytics'] = True
         r = self.app.post('/adobe/_admin/update',
                           params=dict(name='Mozq1', css='',
                                       homepage='# MozQ1!', tracking_id='U-123456'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         r = self.app.post('/adobe/_admin/update',
                           params=dict(name='Mozq1', css='',
                                       homepage='# MozQ1!\n[Root]'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         # make sure project_template is validated as proper json
         r = self.app.post('/adobe/_admin/update',
                           params=dict(project_template='{'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert 'Invalid JSON' in r
 
     def test_admin_overview_audit_log(self):
@@ -106,7 +106,7 @@ class TestNeighborhood(TestController):
 
         }
         self.app.post('/p/_admin/update', params=params,
-                      extra_environ=dict(username='root'))
+                      extra_environ=dict(username=str('root')))
         # must get as many log records as many values are updated
         assert M.AuditLog.query.find().count() == len(params)
 
@@ -128,9 +128,9 @@ class TestNeighborhood(TestController):
         self.app.post('/p/_admin/update',
                       params=dict(name='Projects',
                                   prohibited_tools='wiki, tickets'),
-                      extra_environ=dict(username='root'))
+                      extra_environ=dict(username=str('root')))
 
-        r = self.app.get('/p/_admin/overview', extra_environ=dict(username='root'))
+        r = self.app.get('/p/_admin/overview', extra_environ=dict(username=str('root')))
         assert 'wiki, tickets' in r
 
         c.user = M.User.query.get(username='root')
@@ -143,7 +143,7 @@ class TestNeighborhood(TestController):
         r = self.app.post('/p/_admin/update',
                           params=dict(name='Projects',
                                       prohibited_tools='wiki, test'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert 'error' in self.webflash(r), self.webflash(r)
 
     @td.with_wiki
@@ -153,26 +153,26 @@ class TestNeighborhood(TestController):
         r = self.app.post('/p/_admin/update',
                           params=dict(name='Projects',
                                       anchored_tools='wiki:Wiki, tickets:Ticket'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert 'error' not in self.webflash(r)
         r = self.app.post('/p/_admin/update',
                           params=dict(name='Projects',
                                       anchored_tools='w!iki:Wiki, tickets:Ticket'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert 'error' in self.webflash(r)
         assert_equal(neighborhood.anchored_tools, 'wiki:Wiki, tickets:Ticket')
 
         r = self.app.post('/p/_admin/update',
                           params=dict(name='Projects',
                                       anchored_tools='wiki:Wiki,'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert 'error' in self.webflash(r)
         assert_equal(neighborhood.anchored_tools, 'wiki:Wiki, tickets:Ticket')
 
         r = self.app.post('/p/_admin/update',
                           params=dict(name='Projects',
                                       anchored_tools='badname,'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert 'error' in self.webflash(r)
         assert_equal(neighborhood.anchored_tools, 'wiki:Wiki, tickets:Ticket')
 
@@ -192,7 +192,7 @@ class TestNeighborhood(TestController):
 
     def test_show_title(self):
         r = self.app.get('/adobe/_admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         neighborhood = M.Neighborhood.query.get(name='Adobe')
         # if not set show_title must be True
         assert neighborhood.show_title
@@ -203,17 +203,17 @@ class TestNeighborhood(TestController):
                                       homepage='# MozQ1!',
                                       tracking_id='U-123456',
                                       show_title='false'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         # no title now
-        r = self.app.get('/adobe/', extra_environ=dict(username='root'))
+        r = self.app.get('/adobe/', extra_environ=dict(username=str('root')))
         assert 'class="project_title"' not in str(r)
         r = self.app.get('/adobe/wiki/Home/',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'class="project_title"' not in str(r)
 
         # title must be present on project page
         r = self.app.get('/adobe/adobe-1/admin/',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'class="project_title"' in str(r)
 
     def test_admin_stats_del_count(self):
@@ -222,7 +222,7 @@ class TestNeighborhood(TestController):
         proj.deleted = True
         ThreadLocalORMSession.flush_all()
         r = self.app.get('/adobe/_admin/stats/',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'Deleted: 1' in r
         assert 'Private: 0' in r
 
@@ -233,7 +233,7 @@ class TestNeighborhood(TestController):
         proj.private = True
         ThreadLocalORMSession.flush_all()
         r = self.app.get('/adobe/_admin/stats/',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'Deleted: 0' in r
         assert 'Private: 1' in r
 
@@ -243,7 +243,7 @@ class TestNeighborhood(TestController):
         proj.private = False
         ThreadLocalORMSession.flush_all()
         r = self.app.get('/adobe/_admin/stats/adminlist',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         pq = M.Project.query.find(
             dict(neighborhood_id=neighborhood._id, deleted=False))
         pq.sort('name')
@@ -267,11 +267,11 @@ class TestNeighborhood(TestController):
         file_data = file(file_path).read()
         upload = ('icon', file_name, file_data)
 
-        r = self.app.get('/adobe/_admin/', extra_environ=dict(username='root'))
+        r = self.app.get('/adobe/_admin/', extra_environ=dict(username=str('root')))
         r = self.app.post('/adobe/_admin/update',
                           params=dict(name='Mozq1', css='',
                                       homepage='# MozQ1'),
-                          extra_environ=dict(username='root'), upload_files=[upload])
+                          extra_environ=dict(username=str('root')), upload_files=[upload])
         r = self.app.get('/adobe/icon')
         image = PIL.Image.open(StringIO(r.body))
         assert image.size == (48, 48)
@@ -283,33 +283,33 @@ class TestNeighborhood(TestController):
         neighborhood = M.Neighborhood.query.get(name='Adobe')
         neighborhood.features['google_analytics'] = True
         r = self.app.get('/adobe/_admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'Google Analytics ID' in r
         r = self.app.get('/adobe/adobe-1/admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'Google Analytics ID' in r
         r = self.app.post('/adobe/_admin/update',
                           params=dict(name='Adobe', css='',
                                       homepage='# MozQ1', tracking_id='U-123456'),
-                          extra_environ=dict(username='root'), status=302)
+                          extra_environ=dict(username=str('root')), status=302)
         r = self.app.post('/adobe/adobe-1/admin/update',
                           params=dict(tracking_id='U-654321'),
-                          extra_environ=dict(username='root'), status=302)
+                          extra_environ=dict(username=str('root')), status=302)
         r = self.app.get('/adobe/adobe-1/admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert "_add_tracking('nbhd', 'U-123456');" in r, r
         assert "_add_tracking('proj', 'U-654321');" in r
         # analytics not allowed
         neighborhood = M.Neighborhood.query.get(name='Adobe')
         neighborhood.features['google_analytics'] = False
         r = self.app.get('/adobe/_admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'Google Analytics ID' not in r
         r = self.app.get('/adobe/adobe-1/admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'Google Analytics ID' not in r
         r = self.app.get('/adobe/adobe-1/admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert "_add_tracking('nbhd', 'U-123456');" not in r
         assert "_add_tracking('proj', 'U-654321');" not in r
 
@@ -323,7 +323,7 @@ class TestNeighborhood(TestController):
         r = self.app.get('/adobe/')
         assert test_css not in r
         r = self.app.get('/adobe/_admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert custom_css not in r
 
         neighborhood = M.Neighborhood.query.get(name='Adobe')
@@ -333,7 +333,7 @@ class TestNeighborhood(TestController):
             r = r.follow()
         assert test_css in r
         r = self.app.get('/adobe/_admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert custom_css in r
 
         neighborhood = M.Neighborhood.query.get(name='Adobe')
@@ -343,7 +343,7 @@ class TestNeighborhood(TestController):
             r = r.follow()
         assert test_css in r
         r = self.app.get('/adobe/_admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert custom_css in r
 
     def test_picker_css(self):
@@ -351,7 +351,7 @@ class TestNeighborhood(TestController):
         neighborhood.features['css'] = 'picker'
 
         r = self.app.get('/adobe/_admin/overview',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'Project title, font' in r
         assert 'Project title, color' in r
         assert 'Bar on top' in r
@@ -367,7 +367,7 @@ class TestNeighborhood(TestController):
                                   'css-barontop': '#555555',
                                   'css-titlebarbackground': '#333',
                                   'css-titlebarcolor': '#444'},
-                          extra_environ=dict(username='root'), upload_files=[])
+                          extra_environ=dict(username=str('root')), upload_files=[])
         neighborhood = M.Neighborhood.query.get(name='Adobe')
         assert '/*projecttitlefont*/.project_title{font-family:arial,sans-serif;}' in neighborhood.css
         assert '/*projecttitlecolor*/.project_title{color:green;}' in neighborhood.css
@@ -384,7 +384,7 @@ class TestNeighborhood(TestController):
                               project_unixname='maxproject1', project_name='Max project1',
                               project_description='', neighborhood='Projects'),
                           antispam=True,
-                          extra_environ=dict(username='root'), status=302)
+                          extra_environ=dict(username=str('root')), status=302)
         assert '/p/maxproject1/admin' in r.location
 
         # Set max value to 0
@@ -395,7 +395,7 @@ class TestNeighborhood(TestController):
                               project_unixname='maxproject2', project_name='Max project2',
                               project_description='', neighborhood='Projects'),
                           antispam=True,
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         while isinstance(r.response, HTTPFound):
             r = r.follow()
         assert 'You have exceeded the maximum number of projects' in r
@@ -408,7 +408,7 @@ class TestNeighborhood(TestController):
                                   project_unixname='rateproject1', project_name='Rate project1',
                                   project_description='', neighborhood='Projects'),
                               antispam=True,
-                              extra_environ=dict(username='test-user-1'), status=302)
+                              extra_environ=dict(username=str('test-user-1')), status=302)
             assert '/p/rateproject1/admin' in r.location
 
         # Set rate limit to 1 in first hour of user account
@@ -418,7 +418,7 @@ class TestNeighborhood(TestController):
                                   project_unixname='rateproject2', project_name='Rate project2',
                                   project_description='', neighborhood='Projects'),
                               antispam=True,
-                              extra_environ=dict(username='test-user-1'))
+                              extra_environ=dict(username=str('test-user-1')))
             while isinstance(r.response, HTTPFound):
                 r = r.follow()
             assert 'Project creation rate limit exceeded.  Please try again later.' in r
@@ -431,7 +431,7 @@ class TestNeighborhood(TestController):
                                   project_unixname='rateproject1', project_name='Rate project1',
                                   project_description='', neighborhood='Projects'),
                               antispam=True,
-                              extra_environ=dict(username='root'), status=302)
+                              extra_environ=dict(username=str('root')), status=302)
             assert '/p/rateproject1/admin' in r.location
 
         # Set rate limit to 1 in first hour of user account
@@ -441,71 +441,71 @@ class TestNeighborhood(TestController):
                                   project_unixname='rateproject2', project_name='Rate project2',
                                   project_description='', neighborhood='Projects'),
                               antispam=True,
-                              extra_environ=dict(username='root'))
+                              extra_environ=dict(username=str('root')))
             assert '/p/rateproject2/admin' in r.location
 
     def test_invite(self):
         p_nbhd_id = str(M.Neighborhood.query.get(name='Projects')._id)
         r = self.app.get('/adobe/_moderate/',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         r = self.app.post('/adobe/_moderate/invite',
                           params=dict(pid='adobe-1', invite='on',
                                       neighborhood_id=p_nbhd_id),
-                          extra_environ=dict(username='root'))
-        r = self.app.get(r.location, extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
+        r = self.app.get(r.location, extra_environ=dict(username=str('root')))
         assert 'error' in r
         r = self.app.post('/adobe/_moderate/invite',
                           params=dict(pid='no_such_user',
                                       invite='on', neighborhood_id=p_nbhd_id),
-                          extra_environ=dict(username='root'))
-        r = self.app.get(r.location, extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
+        r = self.app.get(r.location, extra_environ=dict(username=str('root')))
         assert 'error' in r
         r = self.app.post('/adobe/_moderate/invite',
                           params=dict(pid='test', invite='on',
                                       neighborhood_id=p_nbhd_id),
-                          extra_environ=dict(username='root'))
-        r = self.app.get(r.location, extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
+        r = self.app.get(r.location, extra_environ=dict(username=str('root')))
         assert 'invited' in r, r
         assert 'warning' not in r
         r = self.app.post('/adobe/_moderate/invite',
                           params=dict(pid='test', invite='on',
                                       neighborhood_id=p_nbhd_id),
-                          extra_environ=dict(username='root'))
-        r = self.app.get(r.location, extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
+        r = self.app.get(r.location, extra_environ=dict(username=str('root')))
         assert 'warning' in r
         r = self.app.post('/adobe/_moderate/invite',
                           params=dict(pid='test', uninvite='on',
                                       neighborhood_id=p_nbhd_id),
-                          extra_environ=dict(username='root'))
-        r = self.app.get(r.location, extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
+        r = self.app.get(r.location, extra_environ=dict(username=str('root')))
         assert 'uninvited' in r
         assert 'warning' not in r
         r = self.app.post('/adobe/_moderate/invite',
                           params=dict(pid='test', uninvite='on',
                                       neighborhood_id=p_nbhd_id),
-                          extra_environ=dict(username='root'))
-        r = self.app.get(r.location, extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
+        r = self.app.get(r.location, extra_environ=dict(username=str('root')))
         assert 'warning' in r
         r = self.app.post('/adobe/_moderate/invite',
                           params=dict(pid='test', invite='on',
                                       neighborhood_id=p_nbhd_id),
-                          extra_environ=dict(username='root'))
-        r = self.app.get(r.location, extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
+        r = self.app.get(r.location, extra_environ=dict(username=str('root')))
         assert 'invited' in r
         assert 'warning' not in r
 
     def test_evict(self):
         r = self.app.get('/adobe/_moderate/',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         r = self.app.post('/adobe/_moderate/evict',
                           params=dict(pid='test'),
-                          extra_environ=dict(username='root'))
-        r = self.app.get(r.location, extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
+        r = self.app.get(r.location, extra_environ=dict(username=str('root')))
         assert 'error' in r
         r = self.app.post('/adobe/_moderate/evict',
                           params=dict(pid='adobe-1'),
-                          extra_environ=dict(username='root'))
-        r = self.app.get(r.location, extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
+        r = self.app.get(r.location, extra_environ=dict(username=str('root')))
         assert 'adobe-1 evicted to Projects' in r
 
     def test_home(self):
@@ -518,7 +518,7 @@ class TestNeighborhood(TestController):
                               project_unixname='', project_name='Nothing',
                               project_description='', neighborhood='Adobe'),
                           antispam=True,
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert r.html.find('div', {'class': 'error'}
                            ).string == 'Please use 3-15 small letters, numbers, and dashes.'
         r = self.app.post('/adobe/register',
@@ -526,14 +526,14 @@ class TestNeighborhood(TestController):
                               project_unixname='mymoz', project_name='My Moz',
                               project_description='', neighborhood='Adobe'),
                           antispam=True,
-                          extra_environ=dict(username='*anonymous'),
+                          extra_environ=dict(username=str('*anonymous')),
                           status=302)
         r = self.app.post('/adobe/register',
                           params=dict(
                               project_unixname='foo.mymoz', project_name='My Moz',
                               project_description='', neighborhood='Adobe'),
                           antispam=True,
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert r.html.find('div', {'class': 'error'}
                            ).string == 'Please use 3-15 small letters, numbers, and dashes.'
         r = self.app.post('/p/register',
@@ -541,7 +541,7 @@ class TestNeighborhood(TestController):
                               project_unixname='test', project_name='Tester',
                               project_description='', neighborhood='Projects'),
                           antispam=True,
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert r.html.find('div', {'class': 'error'}
                            ).string == 'This project name is taken.'
         r = self.app.post('/adobe/register',
@@ -549,7 +549,7 @@ class TestNeighborhood(TestController):
                               project_unixname='mymoz', project_name='My Moz',
                               project_description='', neighborhood='Adobe'),
                           antispam=True,
-                          extra_environ=dict(username='root'),
+                          extra_environ=dict(username=str('root')),
                           status=302)
 
     def test_register_private_fails_for_anon(self):
@@ -562,7 +562,7 @@ class TestNeighborhood(TestController):
                 neighborhood='Projects',
                 private_project='on'),
             antispam=True,
-            extra_environ=dict(username='*anonymous'),
+            extra_environ=dict(username=str('*anonymous')),
             status=302)
         assert config.get('auth.login_url', '/auth/') in r.location, r.location
 
@@ -576,14 +576,14 @@ class TestNeighborhood(TestController):
                 neighborhood='Projects',
                 private_project='on'),
             antispam=True,
-            extra_environ=dict(username='test-user'),
+            extra_environ=dict(username=str('test-user')),
             status=403)
 
     def test_register_private_fails_for_non_private_neighborhood(self):
         # Turn off private
         neighborhood = M.Neighborhood.query.get(name='Projects')
         neighborhood.features['private_projects'] = False
-        r = self.app.get('/p/add_project', extra_environ=dict(username='root'))
+        r = self.app.get('/p/add_project', extra_environ=dict(username=str('root')))
         assert 'private_project' not in r
 
         r = self.app.post(
@@ -595,7 +595,7 @@ class TestNeighborhood(TestController):
                 neighborhood='Projects',
                 private_project='on'),
             antispam=True,
-            extra_environ=dict(username='root'))
+            extra_environ=dict(username=str('root')))
         cookies = r.headers.getall('Set-Cookie')
         flash_msg_cookies = map(urllib2.unquote, cookies)
 
@@ -608,7 +608,7 @@ class TestNeighborhood(TestController):
         # Turn on private
         neighborhood = M.Neighborhood.query.get(name='Projects')
         neighborhood.features['private_projects'] = True
-        r = self.app.get('/p/add_project', extra_environ=dict(username='root'))
+        r = self.app.get('/p/add_project', extra_environ=dict(username=str('root')))
         assert 'private_project' in r
 
         self.app.post(
@@ -620,7 +620,7 @@ class TestNeighborhood(TestController):
                 neighborhood='Projects',
                 private_project='on'),
             antispam=True,
-            extra_environ=dict(username='root'))
+            extra_environ=dict(username=str('root')))
 
         proj = M.Project.query.get(
             shortname='myprivate2', neighborhood_id=neighborhood._id)
@@ -637,21 +637,21 @@ class TestNeighborhood(TestController):
                 private_project='on',
                 tools='wiki'),
             antispam=True,
-            extra_environ=dict(username='root'),
+            extra_environ=dict(username=str('root')),
             status=302)
         assert config.get('auth.login_url',
                           '/auth/') not in r.location, r.location
         r = self.app.get(
             '/p/mymoz/wiki/',
-            extra_environ=dict(username='root')).follow(extra_environ=dict(username='root'), status=200)
+            extra_environ=dict(username=str('root'))).follow(extra_environ=dict(username=str('root')), status=200)
         r = self.app.get(
             '/p/mymoz/wiki/',
-            extra_environ=dict(username='*anonymous'),
+            extra_environ=dict(username=str('*anonymous')),
             status=302)
         assert config.get('auth.login_url', '/auth/') in r.location, r.location
         self.app.get(
             '/p/mymoz/wiki/',
-            extra_environ=dict(username='test-user'),
+            extra_environ=dict(username=str('test-user')),
             status=403)
 
     def test_project_template(self):
@@ -714,7 +714,7 @@ class TestNeighborhood(TestController):
                 },
                 "groups": %s
                 }""" % (icon_url, json.dumps(test_groups))),
-            extra_environ=dict(username='root'))
+            extra_environ=dict(username=str('root')))
         r = self.app.post(
             '/adobe/register',
             params=dict(
@@ -724,7 +724,7 @@ class TestNeighborhood(TestController):
                 neighborhood='Mozq1',
                 private_project='off'),
             antispam=True,
-            extra_environ=dict(username='root'),
+            extra_environ=dict(username=str('root')),
             status=302).follow()
         p = M.Project.query.get(shortname='testtemp')
         # make sure the correct tools got installed in the right order
@@ -740,10 +740,10 @@ class TestNeighborhood(TestController):
         # make sure project is private
         r = self.app.get(
             '/adobe/testtemp/wiki/',
-            extra_environ=dict(username='root')).follow(extra_environ=dict(username='root'), status=200)
+            extra_environ=dict(username=str('root'))).follow(extra_environ=dict(username=str('root')), status=200)
         r = self.app.get(
             '/adobe/testtemp/wiki/',
-            extra_environ=dict(username='*anonymous'),
+            extra_environ=dict(username=str('*anonymous')),
             status=302)
         # check the labels and trove cats
         r = self.app.get('/adobe/testtemp/admin/trove')
@@ -809,7 +809,7 @@ class TestNeighborhood(TestController):
                 "tool_order":["wiki","admin"],
 
                 }"""),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         neighborhood = M.Neighborhood.query.get(name='Adobe')
         neighborhood.anchored_tools = 'wiki:Wiki'
         r = self.app.post(
@@ -821,7 +821,7 @@ class TestNeighborhood(TestController):
                 neighborhood='Adobe',
                 private_project='off'),
             antispam=True,
-            extra_environ=dict(username='root'))
+            extra_environ=dict(username=str('root')))
         r = self.app.get('/adobe/testtemp/admin/overview')
         assert r.html.find('div', id='top_nav').find(
             'a', href='/adobe/testtemp/wiki/'), r.html
@@ -858,7 +858,7 @@ class TestNeighborhood(TestController):
                               project_unixname='test', project_name='Test again',
                               project_description='', neighborhood='Adobe', tools='wiki'),
                           antispam=True,
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         assert r.status_int == 302, r.html.find(
             'div', {'class': 'error'}).string
         r = self.app.get('/adobe/test/wiki/').follow(status=200)
@@ -871,42 +871,42 @@ class TestNeighborhood(TestController):
         upload = ('icon', file_name, file_data)
 
         r = self.app.get('/adobe/_admin/awards',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         r = self.app.post('/adobe/_admin/awards/create',
                           params=dict(short='FOO', full='A basic foo award'),
-                          extra_environ=dict(username='root'), upload_files=[upload])
+                          extra_environ=dict(username=str('root')), upload_files=[upload])
         r = self.app.post('/adobe/_admin/awards/create',
                           params=dict(short='BAR',
                                       full='A basic bar award with no icon'),
-                          extra_environ=dict(username='root'))
+                          extra_environ=dict(username=str('root')))
         foo_id = str(M.Award.query.find(dict(short='FOO')).first()._id)
         bar_id = str(M.Award.query.find(dict(short='BAR')).first()._id)
         r = self.app.post('/adobe/_admin/awards/%s/update' % bar_id,
                           params=dict(short='BAR2',
                                       full='Updated description.'),
-                          extra_environ=dict(username='root')).follow().follow()
+                          extra_environ=dict(username=str('root'))).follow().follow()
         assert 'BAR2' in r
         assert 'Updated description.' in r
         r = self.app.get('/adobe/_admin/awards/%s' %
-                         foo_id, extra_environ=dict(username='root'))
+                         foo_id, extra_environ=dict(username=str('root')))
         r = self.app.get('/adobe/_admin/awards/%s/icon' %
-                         foo_id, extra_environ=dict(username='root'))
+                         foo_id, extra_environ=dict(username=str('root')))
         image = PIL.Image.open(StringIO(r.body))
         assert image.size == (48, 48)
         self.app.post('/adobe/_admin/awards/grant',
                       params=dict(grant='FOO', recipient='adobe-1',
                                   url='http://award.org', comment='Winner!'),
-                      extra_environ=dict(username='root'))
+                      extra_environ=dict(username=str('root')))
         r = self.app.get('/adobe/_admin/accolades',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert_in('Winner!', r)
         assert_in('http://award.org', r)
         self.app.get('/adobe/_admin/awards/%s/adobe-1' %
-                     foo_id, extra_environ=dict(username='root'))
+                     foo_id, extra_environ=dict(username=str('root')))
         self.app.post('/adobe/_admin/awards/%s/adobe-1/revoke' % foo_id,
-                      extra_environ=dict(username='root'))
+                      extra_environ=dict(username=str('root')))
         self.app.post('/adobe/_admin/awards/%s/delete' % foo_id,
-                      extra_environ=dict(username='root'))
+                      extra_environ=dict(username=str('root')))
 
     def test_add_a_project_link(self):
         from tg import tmpl_context as c
@@ -917,24 +917,24 @@ class TestNeighborhood(TestController):
                 p.install_app('home', 'home', 'Home', ordinal=0)
         r = self.app.get('/p/')
         assert 'Add a Project' in r
-        r = self.app.get('/u/', extra_environ=dict(username='test-user'))
+        r = self.app.get('/u/', extra_environ=dict(username=str('test-user')))
         assert 'Add a Project' not in r
-        r = self.app.get('/adobe/', extra_environ=dict(username='test-user'))
+        r = self.app.get('/adobe/', extra_environ=dict(username=str('test-user')))
         assert 'Add a Project' not in r
-        r = self.app.get('/u/', extra_environ=dict(username='root'))
+        r = self.app.get('/u/', extra_environ=dict(username=str('root')))
         assert 'Add a Project' in r
-        r = self.app.get('/adobe/', extra_environ=dict(username='root'))
+        r = self.app.get('/adobe/', extra_environ=dict(username=str('root')))
         assert 'Add a Project' in r
 
     def test_help(self):
         r = self.app.get('/p/_admin/help/',
-                         extra_environ=dict(username='root'))
+                         extra_environ=dict(username=str('root')))
         assert 'macro' in r
 
     @td.with_user_project('test-user')
     def test_profile_tools(self):
         r = self.app.get('/u/test-user/',
-                         extra_environ=dict(username='test-user')).follow()
+                         extra_environ=dict(username=str('test-user'))).follow()
         assert r.html.find('div', 'profile-section tools').find(
             'a', href='/u/test-user/profile/'), r.html
 
@@ -1086,7 +1086,7 @@ class TestPhoneVerificationOnProjectRegistration(TestController):
                     project_name='Phone Test',
                     project_description='',
                     neighborhood='Projects'),
-                extra_environ=dict(username='test-user'),
+                extra_environ=dict(username=str('test-user')),
                 antispam=True)
             overlay = r.html.find('div', {'id': 'phone_verification_overlay'})
             assert_not_equal(overlay, None)
diff --git a/Allura/allura/tests/functional/test_rest.py b/Allura/allura/tests/functional/test_rest.py
index fc8ba1e..7cbff40 100644
--- a/Allura/allura/tests/functional/test_rest.py
+++ b/Allura/allura/tests/functional/test_rest.py
@@ -201,7 +201,7 @@ class TestRestHome(TestRestApiBase):
 
         # anonymous sees only non-private tool
         r = self.app.get('/rest/p/test/',
-                         extra_environ={'username': '*anonymous'})
+                         extra_environ={'username': str('*anonymous')})
         assert_equal(r.json['shortname'], 'test')
         tool_mounts = [t['mount_point'] for t in r.json['tools']]
         assert_in('bugs', tool_mounts)
@@ -333,10 +333,10 @@ class TestRestHome(TestRestApiBase):
         if auth_read_perm in acl:
             acl.remove(auth_read_perm)
         self.app.get('/rest/p/test/wiki/Home/',
-                     extra_environ={'username': '*anonymous'},
+                     extra_environ={'username': str('*anonymous')},
                      status=401)
         self.app.get('/rest/p/test/wiki/Home/',
-                     extra_environ={'username': 'test-user-0'},
+                     extra_environ={'username': str('test-user-0')},
                      status=403)
 
     def test_index(self):
@@ -368,7 +368,7 @@ class TestRestHome(TestRestApiBase):
     @td.with_wiki
     def test_cors_POST_req_blocked_by_csrf(self):
         # so test-admin isn't automatically logged in for all requests
-        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        self.app.extra_environ = {'disable_auth_magic': str('True')}
 
         # regular login to get a session cookie set up
         r = self.app.get('/auth/')
@@ -380,7 +380,7 @@ class TestRestHome(TestRestApiBase):
         # simulate CORS ajax request withCredentials (cookie headers)
         # make sure we don't allow the cookies to authorize the request (else could be a CSRF attack vector)
         assert self.app.cookies['allura']
-        self.app.post('/rest/p/test/wiki/NewPage', headers={'Origin': 'http://bad.com/'},
+        self.app.post('/rest/p/test/wiki/NewPage', headers={'Origin': str('http://bad.com/')},
                       status=401)
 
     @mock.patch('allura.lib.plugin.ThemeProvider._get_site_notification')
@@ -463,7 +463,7 @@ class TestDoap(TestRestApiBase):
 
         # anonymous sees only non-private tool
         r = self.app.get('/rest/p/test?doap',
-                         extra_environ={'username': '*anonymous'})
+                         extra_environ={'username': str('*anonymous')})
         p = r.xml.find(self.ns + 'Project')
         tools = p.findall(self.ns_sf + 'feature')
         tools = [(t.find(self.ns_sf + 'Feature').find(self.ns + 'name').text,
diff --git a/Allura/allura/tests/functional/test_root.py b/Allura/allura/tests/functional/test_root.py
index 7438a37..b91a97e 100644
--- a/Allura/allura/tests/functional/test_root.py
+++ b/Allura/allura/tests/functional/test_root.py
@@ -56,7 +56,7 @@ class TestRootController(TestController):
         n_adobe.register_project('adobe-2', u_admin)
 
     def test_index(self):
-        response = self.app.get('/', extra_environ=dict(username='*anonymous'))
+        response = self.app.get('/', extra_environ=dict(username=str('*anonymous')))
         assert_equal(response.location, 'http://localhost/neighborhood')
 
         response = self.app.get('/')
@@ -99,7 +99,7 @@ class TestRootController(TestController):
         for hdr in hdrs:
             # malformed headers used to return 500, just make sure they don't
             # now
-            self.app.get('/', headers=dict(Accept=hdr), validate_skip=True)
+            self.app.get('/', headers=dict(Accept=str(hdr)), validate_skip=True)
 
     def test_encoded_urls(self):
         # not valid unicode
@@ -214,7 +214,7 @@ class TestRootWithSSLPattern(TestController):
     def test_no_weird_ssl_redirect_for_error_document(self):
         # test a 404, same functionality as a 500 from an error
         r = self.app.get('/auth/asdfasdf',
-                         extra_environ={'wsgi.url_scheme': 'https'},
+                         extra_environ={'wsgi.url_scheme': str('https')},
                          status=404)
         assert '302 Found' not in r.body, r.body
         assert '/error/document' not in r.body, r.body
diff --git a/Allura/allura/tests/functional/test_search.py b/Allura/allura/tests/functional/test_search.py
index 3d3315b..6a09f5c 100644
--- a/Allura/allura/tests/functional/test_search.py
+++ b/Allura/allura/tests/functional/test_search.py
@@ -70,6 +70,6 @@ class TestSearch(TestController):
         resp.mustcontain('Welcome to your wiki! This is the default page')
         resp.mustcontain('Sample wiki comment')
 
-        resp = self.app.get('/p/test2/search/', params=dict(q='wiki'), extra_environ=dict(username='*anonymous'))
+        resp = self.app.get('/p/test2/search/', params=dict(q='wiki'), extra_environ=dict(username=str('*anonymous')))
         resp.mustcontain(no='Welcome to your wiki! This is the default page')
         resp.mustcontain(no='Sample wiki comment')
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index b75f51d..e287f0a 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -40,21 +40,21 @@ class TestSiteAdmin(TestController):
 
     def test_access(self):
         r = self.app.get('/nf/admin/', extra_environ=dict(
-            username='test-user'), status=403)
+            username=str('test-user')), status=403)
 
         r = self.app.get('/nf/admin/', extra_environ=dict(
-            username='*anonymous'), status=302)
+            username=str('*anonymous')), status=302)
         r = r.follow()
         assert 'Login' in r
 
     def test_home(self):
         r = self.app.get('/nf/admin/', extra_environ=dict(
-            username='root'))
+            username=str('root')))
         assert 'Site Admin Home' in r
 
     def test_stats(self):
         r = self.app.get('/nf/admin/stats/', extra_environ=dict(
-            username='root'))
+            username=str('root')))
         assert 'Forge Site Admin' in r.html.find(
             'h2', {'class': 'dark title'}).contents[0]
         stats_table = r.html.find('table')
@@ -63,18 +63,18 @@ class TestSiteAdmin(TestController):
 
     def test_tickets_access(self):
         self.app.get('/nf/admin/api_tickets', extra_environ=dict(
-            username='test-user'), status=403)
+            username=str('test-user')), status=403)
 
     def test_new_projects_access(self):
         self.app.get('/nf/admin/new_projects', extra_environ=dict(
-            username='test_user'), status=403)
+            username=str('test_user')), status=403)
         r = self.app.get('/nf/admin/new_projects', extra_environ=dict(
-            username='*anonymous'), status=302).follow()
+            username=str('*anonymous')), status=302).follow()
         assert 'Login' in r
 
     def test_new_projects(self):
         r = self.app.get('/nf/admin/new_projects', extra_environ=dict(
-            username='root'))
+            username=str('root')))
         headers = r.html.find('table').findAll('th')
         assert headers[1].contents[0] == 'Created'
         assert headers[2].contents[0] == 'Shortname'
@@ -87,18 +87,18 @@ class TestSiteAdmin(TestController):
     def test_new_projects_deleted_projects(self):
         '''Deleted projects should not be visible here'''
         r = self.app.get('/nf/admin/new_projects', extra_environ=dict(
-            username='root'))
+            username=str('root')))
         count = len(r.html.find('table').findAll('tr'))
         p = M.Project.query.get(shortname='test')
         p.deleted = True
         ThreadLocalORMSession.flush_all()
         r = self.app.get('/nf/admin/new_projects', extra_environ=dict(
-            username='root'))
+            username=str('root')))
         assert_equal(len(r.html.find('table').findAll('tr')), count - 1)
 
     def test_new_projects_daterange_filtering(self):
         r = self.app.get('/nf/admin/new_projects', extra_environ=dict(
-            username='root'))
+            username=str('root')))
         count = len(r.html.find('table').findAll('tr'))
         assert_equal(count, 7)
 
@@ -111,7 +111,7 @@ class TestSiteAdmin(TestController):
 
     def test_reclone_repo_access(self):
         r = self.app.get('/nf/admin/reclone_repo', extra_environ=dict(
-            username='*anonymous'), status=302).follow()
+            username=str('*anonymous')), status=302).follow()
         assert 'Login' in r
 
     def test_reclone_repo(self):
@@ -124,7 +124,7 @@ class TestSiteAdmin(TestController):
 
     def test_task_list(self):
         r = self.app.get('/nf/admin/task_manager',
-                         extra_environ=dict(username='*anonymous'), status=302)
+                         extra_environ=dict(username=str('*anonymous')), status=302)
         import math
         M.MonQTask.post(math.ceil, (12.5,))
         r = self.app.get('/nf/admin/task_manager?page_num=1')
@@ -135,7 +135,7 @@ class TestSiteAdmin(TestController):
         task = M.MonQTask.post(math.ceil, (12.5,))
         url = '/nf/admin/task_manager/view/%s' % task._id
         r = self.app.get(
-            url, extra_environ=dict(username='*anonymous'), status=302)
+            url, extra_environ=dict(username=str('*anonymous')), status=302)
         r = self.app.get(url)
         assert 'math.ceil' in r, r
         assert 'ready' in r, r
@@ -185,9 +185,9 @@ class TestSiteAdminNotifications(TestController):
 
     def test_site_notifications_access(self):
         self.app.get('/nf/admin/site_notifications', extra_environ=dict(
-            username='test_user'), status=403)
+            username=str('test_user')), status=403)
         r = self.app.get('/nf/admin/site_notifications', extra_environ=dict(
-            username='*anonymous'), status=302).follow()
+            username=str('*anonymous')), status=302).follow()
         assert 'Login' in r
 
     def test_site_notifications(self):
@@ -201,7 +201,7 @@ class TestSiteAdminNotifications(TestController):
         assert M.notification.SiteNotification.query.find().count() == 1
 
         r = self.app.get('/nf/admin/site_notifications/', extra_environ=dict(
-            username='root'))
+            username=str('root')))
         table = r.html.find('table')
         headers = table.findAll('th')
         row = table.findAll('td')
@@ -496,7 +496,7 @@ class TestUserDetails(TestController):
     def test_add_audit_trail_entry_access(self):
         self.app.get('/nf/admin/user/add_audit_log_entry', status=404)  # GET is not allowed
         r = self.app.post('/nf/admin/user/add_audit_log_entry',
-                          extra_environ={'username': '*anonymous'},
+                          extra_environ={'username': str('*anonymous')},
                           status=302)
         assert_equal(r.location, 'http://localhost/auth/')
 
@@ -649,7 +649,7 @@ class TestUserDetails(TestController):
                 'new_addr.addr': 'test@example.com',
                 'new_addr.claim': 'Claim Address',
                 'primary_addr': 'test@example.com'},
-                extra_environ=dict(username='test-admin'))
+                extra_environ=dict(username=str('test-admin')))
         r = self.app.get('/nf/admin/user/test-user')
         assert_in('test@example.com', r)
         em = M.EmailAddress.get(email='test@example.com')
@@ -664,7 +664,7 @@ class TestUserDetails(TestController):
                 'new_addr.addr': 'test2@example.com',
                 'new_addr.claim': 'Claim Address',
                 'primary_addr': 'test@example.com'},
-                extra_environ=dict(username='test-admin'))
+                extra_environ=dict(username=str('test-admin')))
         r = self.app.get('/nf/admin/user/test-user')
         assert_in('test2@example.com', r)
         em = M.EmailAddress.get(email='test2@example.com')
@@ -678,7 +678,7 @@ class TestUserDetails(TestController):
                 'username': 'test-user',
                 'new_addr.addr': '',
                 'primary_addr': 'test2@example.com'},
-                extra_environ=dict(username='test-admin'))
+                extra_environ=dict(username=str('test-admin')))
         r = self.app.get('/nf/admin/user/test-user')
         user = M.User.query.get(username='test-user')
         assert_equal(user.get_pref('email_address'), 'test2@example.com')
@@ -693,7 +693,7 @@ class TestUserDetails(TestController):
                 'addr-3.delete': 'on',
                 'new_addr.addr': '',
                 'primary_addr': 'test2@example.com'},
-                extra_environ=dict(username='test-admin'))
+                extra_environ=dict(username=str('test-admin')))
         r = self.app.get('/nf/admin/user/test-user')
         user = M.User.query.get(username='test-user')
         # test@example.com set as primary since test2@example.com is deleted
diff --git a/Allura/allura/tests/functional/test_trovecategory.py b/Allura/allura/tests/functional/test_trovecategory.py
index 850a3bb..8946043 100644
--- a/Allura/allura/tests/functional/test_trovecategory.py
+++ b/Allura/allura/tests/functional/test_trovecategory.py
@@ -63,7 +63,7 @@ class TestTroveCategory(TestController):
     def test_enableediting_setting(self):
         def check_access(username=None, status=None):
             self.app.get('/categories/', status=status,
-                         extra_environ=dict(username=username))
+                         extra_environ=dict(username=str(username)))
 
         cfg = {'trovecategories.enableediting': 'true'}
 
diff --git a/Allura/allura/tests/functional/test_user_profile.py b/Allura/allura/tests/functional/test_user_profile.py
index 6ed96a8..5ef92a1 100644
--- a/Allura/allura/tests/functional/test_user_profile.py
+++ b/Allura/allura/tests/functional/test_user_profile.py
@@ -150,7 +150,7 @@ class TestUserProfile(TestController):
     @td.with_user_project('test-user')
     def test_send_message_for_anonymous(self):
         r = self.app.get('/u/test-user/profile/send_message',
-                         extra_environ={'username': '*anonymous'},
+                         extra_environ={'username': str('*anonymous')},
                          status=302)
         assert 'You must be logged in to send user messages.' in self.webflash(
             r)
@@ -159,7 +159,7 @@ class TestUserProfile(TestController):
                           params={'subject': 'test subject',
                                   'message': 'test message',
                                   'cc': 'on'},
-                          extra_environ={'username': '*anonymous'},
+                          extra_environ={'username': str('*anonymous')},
                           status=302)
         assert 'You must be logged in to send user messages.' in self.webflash(
             r)
diff --git a/Allura/allura/tests/model/test_auth.py b/Allura/allura/tests/model/test_auth.py
index 88df750..57c36f6 100644
--- a/Allura/allura/tests/model/test_auth.py
+++ b/Allura/allura/tests/model/test_auth.py
@@ -339,7 +339,7 @@ def test_user_track_active():
     assert_equal(c.user.last_access['session_ip'], None)
     assert_equal(c.user.last_access['session_ua'], None)
 
-    req = Mock(headers={'User-Agent': 'browser'}, remote_addr='addr')
+    req = Mock(headers={'User-Agent': str('browser')}, remote_addr='addr')
     c.user.track_active(req)
     c.user = M.User.by_username(c.user.username)
     assert_not_equal(c.user.last_access['session_date'], None)
diff --git a/Allura/allura/tests/model/test_filesystem.py b/Allura/allura/tests/model/test_filesystem.py
index f8f1e7c..b36f9a2 100644
--- a/Allura/allura/tests/model/test_filesystem.py
+++ b/Allura/allura/tests/model/test_filesystem.py
@@ -148,7 +148,7 @@ class TestFile(TestCase):
             assert_equal(['test1'], response_body)
             assert_equal(response.content_type, f.content_type)
             assert_equal(response.headers['Content-Disposition'],
-                         'attachment;filename="te s\xe0\xad\xae1.txt"')
+                         'attachment;filename="te%20s%E0%AD%AE1.txt"')
 
     def test_image(self):
         path = os.path.join(
diff --git a/Allura/allura/tests/test_security.py b/Allura/allura/tests/test_security.py
index b57ee05..1acb87f 100644
--- a/Allura/allura/tests/test_security.py
+++ b/Allura/allura/tests/test_security.py
@@ -69,7 +69,7 @@ class TestSecurity(TestController):
                      status=200)
         # This should fail b/c test-user doesn't have the permission
         self.app.get('/security/test-user/needs_artifact_access_fail',
-                     extra_environ=dict(username='test-user'), status=403)
+                     extra_environ=dict(username=str('test-user')), status=403)
         # This should succeed b/c users with the 'admin' permission on a
         # project implicitly have all permissions to everything in the project
         self.app.get(
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index a1fad06..9dbba01 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -182,10 +182,10 @@ class TestWebhookController(TestController):
     def test_access(self):
         self.app.get(self.url + '/repo-push/')
         self.app.get(self.url + '/repo-push/',
-                     extra_environ={'username': 'test-user'},
+                     extra_environ={'username': str('test-user')},
                      status=403)
         r = self.app.get(self.url + '/repo-push/',
-                         extra_environ={'username': '*anonymous'},
+                         extra_environ={'username': str('*anonymous')},
                          status=302)
         assert_equal(r.location,
                      'http://localhost/auth/'
@@ -451,7 +451,7 @@ class TestSendWebhookHelper(TestWebhookBase):
         response = Mock(
             status_code=500,
             text='that is why',
-            headers={'Content-Type': 'application/json'})
+            headers={str('Content-Type'): str('application/json')})
         assert_equal(
             self.h.log_msg('Error', response=response),
             "Error: repo-push http://httpbin.org/post /adobe/adobe-1/src/ 500 "
diff --git a/Allura/allura/tests/unit/test_ldap_auth_provider.py b/Allura/allura/tests/unit/test_ldap_auth_provider.py
index 1500252..f635dd5 100644
--- a/Allura/allura/tests/unit/test_ldap_auth_provider.py
+++ b/Allura/allura/tests/unit/test_ldap_auth_provider.py
@@ -76,7 +76,7 @@ class TestLdapAuthenticationProvider(object):
             'password': 'test-password',
         }
         self.provider.request.method = 'POST'
-        self.provider.request.body = '&'.join(['%s=%s' % (k,v) for k,v in params.iteritems()])
+        self.provider.request.body = '&'.join(['%s=%s' % (k,v) for k,v in params.iteritems()]).encode('utf-8')
         ldap.dn.escape_dn_chars = lambda x: x
 
         self.provider._login()
@@ -95,7 +95,7 @@ class TestLdapAuthenticationProvider(object):
             'password': 'test-password',
         }
         self.provider.request.method = 'POST'
-        self.provider.request.body = '&'.join(['%s=%s' % (k,v) for k,v in params.iteritems()])
+        self.provider.request.body = '&'.join(['%s=%s' % (k,v) for k,v in params.iteritems()]).encode('utf-8')
         ldap.dn.escape_dn_chars = lambda x: x
         dn = 'uid=%s,ou=people,dc=localdomain' % params['username']
         conn = ldap.initialize.return_value
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index ca46755..6bee064 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -235,7 +235,7 @@ class WebhookRestController(BaseController):
     @expose('json:')
     @require_post()
     def index(self, **kw):
-        response.content_type = 'application/json'
+        response.content_type = str('application/json')
         try:
             params = {'secret': kw.pop('secret', ''),
                       'url': kw.pop('url', None)}
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 066bf8c..5273060 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -178,7 +178,7 @@ class TestController(object):
         pkg = self.__module__.split('.')[0]
         self.app = ValidatingTestApp(
             setup_functional_test(app_name=self.application_under_test, current_pkg=pkg))
-        self.app.extra_environ = {'REMOTE_ADDR': '127.0.0.1'}  # remote_addr needed by AntiSpam
+        self.app.extra_environ = {str('REMOTE_ADDR'): str('127.0.0.1')}  # remote_addr needed by AntiSpam
         if self.validate_skip:
             self.app.validate_skip = self.validate_skip
         if asbool(tg.config.get('smtp.mock')):
@@ -269,7 +269,7 @@ class TestRestApiBase(TestController):
 
         token = self.token(user).api_key
         headers = {
-            'Authorization': 'Bearer {}'.format(token)
+            'Authorization': str('Bearer {}'.format(token))
         }
 
         fn = getattr(self.app, method.lower())
diff --git a/ForgeActivity/forgeactivity/main.py b/ForgeActivity/forgeactivity/main.py
index 32ddf03..1e6ba97 100644
--- a/ForgeActivity/forgeactivity/main.py
+++ b/ForgeActivity/forgeactivity/main.py
@@ -152,8 +152,8 @@ class ForgeActivityController(BaseController):
     @expose()
     def feed(self, **kw):
         data = self._get_activities_data(**kw)
-        response.headers['Content-Type'] = ''
-        response.content_type = 'application/xml'
+        response.headers['Content-Type'] = str('')
+        response.content_type = str('application/xml')
         d = {
             'title': 'Activity for %s' % data['followee'].activity_name,
             'link': h.absurl(self.app.url),
@@ -161,7 +161,7 @@ class ForgeActivityController(BaseController):
                 data['followee'].activity_name),
             'language': 'en',
         }
-        if request.environ['PATH_INFO'].endswith('.atom'):
+        if request.environ['PATH_INFO'].endswith(str('.atom')):
             feed = FG.Atom1Feed(**d)
         else:
             feed = FG.Rss201rev2Feed(**d)
diff --git a/ForgeActivity/forgeactivity/tests/functional/test_root.py b/ForgeActivity/forgeactivity/tests/functional/test_root.py
index 2f24766..faaac4d 100644
--- a/ForgeActivity/forgeactivity/tests/functional/test_root.py
+++ b/ForgeActivity/forgeactivity/tests/functional/test_root.py
@@ -55,7 +55,7 @@ class TestActivityController(TestController):
     @td.with_user_project('test-user-1')
     def test_anon_read(self):
         r = self.app.get('/u/test-user-1',
-                extra_environ={'username': '*anonymous'}).follow().follow()
+                extra_environ={'username': str('*anonymous')}).follow().follow()
         assert r.html.find('div', 'profile-section tools').find('a',
                 dict(href='/u/test-user-1/activity/')), \
                         'No Activity tool in top nav'
@@ -165,7 +165,7 @@ class TestActivityController(TestController):
     @td.with_user_project('test-user-1')
     def test_background_aggregation(self):
         self.app.post('/u/test-admin/activity/follow', {'follow': 'true'},
-                      extra_environ=dict(username='test-user-1'))
+                      extra_environ=dict(username=str('test-user-1')))
         # new ticket, creates activity
         d = {'ticket_form.summary': 'New Ticket'}
         self.app.post('/bugs/save_ticket', params=d)
@@ -429,7 +429,7 @@ class TestActivityController(TestController):
     def test_delete_item_gone(self):
         self.app.post('/u/test-user-1/activity/delete_item',
                       {'activity_id': str(ObjectId())},
-                      extra_environ={'username': 'root'},  # nbhd admin
+                      extra_environ={'username': str('root')},  # nbhd admin
                       status=410)
 
     @td.with_tool('u/test-user-1', 'activity')
@@ -475,7 +475,7 @@ class TestActivityController(TestController):
 
         self.app.post('/u/test-user-1/activity/delete_item',
                       {'activity_id': activity_id},
-                      extra_environ={'username': 'root'},  # nbhd admin
+                      extra_environ={'username': str('root')},  # nbhd admin
                       status=200)
         ThreadLocalODMSession.flush_all()
 
diff --git a/ForgeBlog/forgeblog/main.py b/ForgeBlog/forgeblog/main.py
index 30b85c2..e04530d 100644
--- a/ForgeBlog/forgeblog/main.py
+++ b/ForgeBlog/forgeblog/main.py
@@ -319,7 +319,7 @@ class RootController(BaseController, FeedController):
         if attachment is not None:
             post.add_multiple_attachments(attachment)
         notification_tasks.send_usermentions_notification.post(post.index_id(), kw['text'])
-        redirect(h.really_unicode(post.url()).encode('utf-8'))
+        redirect(h.urlquote(h.really_unicode(post.url())))
 
     @with_trailing_slash
     @expose('jinja:allura:templates/markdown_syntax_dialog.html')
@@ -426,7 +426,7 @@ class PostController(BaseController, FeedController):
         if delete:
             self.post.delete()
             flash('Post deleted', 'info')
-            redirect(h.really_unicode(c.app.url).encode('utf-8'))
+            redirect(h.urlquote(h.really_unicode(c.app.url)))
         else:
             g.spam_checker.check(kw['title'] + '\n' + kw['text'], artifact=self.post,
                                  user=c.user, content_type='blog-post')
@@ -593,7 +593,7 @@ class RootRestController(BaseController, AppRestControllerMixin):
                 text=text,
                 labels=labels.split(','),
                 **kw)
-            return exc.HTTPCreated(headers=dict(Location=h.absurl('/rest' + post.url()).encode('utf-8')))
+            return exc.HTTPCreated(headers=dict(Location=str(h.absurl('/rest' + post.url()).encode('utf-8'))))
 
         else:
             result = RootController().index(limit=limit, page=page)
diff --git a/ForgeBlog/forgeblog/tests/functional/test_rest.py b/ForgeBlog/forgeblog/tests/functional/test_rest.py
index a2630e7..b38018b 100644
--- a/ForgeBlog/forgeblog/tests/functional/test_rest.py
+++ b/ForgeBlog/forgeblog/tests/functional/test_rest.py
@@ -105,21 +105,21 @@ class TestBlogApi(TestRestApiBase):
         self.api_post('/rest/p/test/blog/', title='test',
                       text='test text', state='published')
         self.app.get('/rest/p/test/blog/',
-                     extra_environ={'username': '*anonymous'}, status=200)
+                     extra_environ={'username': str('*anonymous')}, status=200)
         p = M.Project.query.get(shortname='test')
         acl = p.app_instance('blog').config.acl
         anon = M.ProjectRole.by_name('*anonymous')._id
         anon_read = M.ACE.allow(anon, 'read')
         acl.remove(anon_read)
         self.app.get('/rest/p/test/blog/',
-                     extra_environ={'username': '*anonymous'},
+                     extra_environ={'username': str('*anonymous')},
                      status=401)
 
     def test_new_post_permissons(self):
         self.app.post('/rest/p/test/blog/',
                       params=dict(title='test', text='test text',
                                   state='published'),
-                      extra_environ={'username': '*anonymous'},
+                      extra_environ={'username': str('*anonymous')},
                       status=401)
         p = M.Project.query.get(shortname='test')
         acl = p.app_instance('blog').config.acl
@@ -129,7 +129,7 @@ class TestBlogApi(TestRestApiBase):
         self.app.post('/rest/p/test/blog/',
                       params=dict(title='test', text='test text',
                                   state='published'),
-                      extra_environ={'username': '*anonymous'},
+                      extra_environ={'username': str('*anonymous')},
                       status=201)
 
     def test_update_post_permissons(self):
@@ -139,7 +139,7 @@ class TestBlogApi(TestRestApiBase):
         self.app.post(url.encode('utf-8'),
                       params=dict(title='test2', text='test text2',
                                   state='published'),
-                      extra_environ={'username': '*anonymous'},
+                      extra_environ={'username': str('*anonymous')},
                       status=401)
         p = M.Project.query.get(shortname='test')
         acl = p.app_instance('blog').config.acl
@@ -149,7 +149,7 @@ class TestBlogApi(TestRestApiBase):
         self.app.post(url.encode('utf-8'),
                       params=dict(title='test2', text='test text2',
                                   state='published'),
-                      extra_environ={'username': '*anonymous'},
+                      extra_environ={'username': str('*anonymous')},
                       status=200)
         r = self.api_get(url)
         assert_equal(r.json['title'], 'test2')
@@ -160,13 +160,13 @@ class TestBlogApi(TestRestApiBase):
         self.api_post('/rest/p/test/blog/', title='test',
                       text='test text', state='draft')
         r = self.app.get('/rest/p/test/blog/',
-                         extra_environ={'username': '*anonymous'})
+                         extra_environ={'username': str('*anonymous')})
         assert_equal(r.json['posts'], [])
         url = '/rest' + BM.BlogPost.query.find().first().url()
         self.app.post(url.encode('utf-8'),
                       params=dict(title='test2', text='test text2',
                                   state='published'),
-                      extra_environ={'username': '*anonymous'},
+                      extra_environ={'username': str('*anonymous')},
                       status=401)
         p = M.Project.query.get(shortname='test')
         acl = p.app_instance('blog').config.acl
@@ -174,19 +174,19 @@ class TestBlogApi(TestRestApiBase):
         anon_write = M.ACE.allow(anon, 'write')
         acl.append(anon_write)
         r = self.app.get('/rest/p/test/blog/',
-                         extra_environ={'username': '*anonymous'})
+                         extra_environ={'username': str('*anonymous')})
         assert_equal(r.json['posts'][0]['title'], 'test')
 
     def test_draft_post(self):
         self.api_post('/rest/p/test/blog/', title='test',
                       text='test text', state='draft')
         r = self.app.get('/rest/p/test/blog/',
-                         extra_environ={'username': '*anonymous'})
+                         extra_environ={'username': str('*anonymous')})
         assert_equal(r.json['posts'], [])
         url = '/rest' + BM.BlogPost.query.find().first().url()
         self.api_post(url, state='published')
         r = self.app.get('/rest/p/test/blog/',
-                         extra_environ={'username': '*anonymous'})
+                         extra_environ={'username': str('*anonymous')})
         assert_equal(r.json['posts'][0]['title'], 'test')
 
     def test_pagination(self):
diff --git a/ForgeBlog/forgeblog/tests/functional/test_root.py b/ForgeBlog/forgeblog/tests/functional/test_root.py
index 9d112c5..8c61cfa 100644
--- a/ForgeBlog/forgeblog/tests/functional/test_root.py
+++ b/ForgeBlog/forgeblog/tests/functional/test_root.py
@@ -69,7 +69,7 @@ class Test(TestController):
         assert 'Nothing to see here' in response
         assert '/blog/%s/my-post/edit' % d in response
         anon_r = self.app.get('/blog/',
-                              extra_environ=dict(username='*anonymous'))
+                              extra_environ=dict(username=str('*anonymous')))
         # anonymous user can't see Edit links
         assert 'Nothing to see here' in anon_r
         assert '/blog/%s/my-post/edit' % d not in anon_r
@@ -83,7 +83,7 @@ class Test(TestController):
         assert 'Draft' in response
         assert '/blog/%s/my-post/edit' % d in response
         anon_r = self.app.get('/blog/',
-                              extra_environ=dict(username='*anonymous'))
+                              extra_environ=dict(username=str('*anonymous')))
         # anonymous user can't see draft posts
         assert 'Nothing to see here' not in anon_r
 
@@ -136,7 +136,7 @@ class Test(TestController):
         assert 'Nothing to see here' in response
         assert '/blog/%s/my-post/edit' % d in response
         anon_r = self.app.get('/blog/%s/my-post/' % d,
-                              extra_environ=dict(username='*anonymous'))
+                              extra_environ=dict(username=str('*anonymous')))
         # anonymous user can't see Edit links
         assert 'Nothing to see here' in anon_r
         assert '/blog/%s/my-post/edit' % d not in anon_r
@@ -150,7 +150,7 @@ class Test(TestController):
         assert 'Draft' in response
         assert '/blog/%s/my-post/edit' % d in response
         anon_r = self.app.get('/blog/%s/my-post/' % d,
-                              extra_environ=dict(username='*anonymous'))
+                              extra_environ=dict(username=str('*anonymous')))
         # anonymous user can't get to draft posts
         assert 'Nothing to see here' not in anon_r
 
@@ -161,7 +161,7 @@ class Test(TestController):
         assert 'Nothing' in response
         # anon users can't edit
         response = self.app.get('/blog/%s/my-post/edit' % d,
-                                extra_environ=dict(username='*anonymous'))
+                                extra_environ=dict(username=str('*anonymous')))
         assert 'Nothing' not in response
 
     def test_post_get_markdown(self):
@@ -183,7 +183,7 @@ class Test(TestController):
             '/blog/%s/my-post/update_markdown' % d,
             params={
                 'text': '- [x] checkbox'},
-            extra_environ=dict(username='*anonymous'))
+            extra_environ=dict(username=str('*anonymous')))
         assert response.json['status'] == 'no_permission'
 
     def test_post_attachments(self):
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
index ff736b1..13c1f07 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
@@ -403,7 +403,7 @@ class TestForum(TestController):
             params[f.find('select')['name']] = 'testforum'
             params[f.find('input', {'style': 'width: 90%'})['name']] = 'Test Zero Posts'
             r = self.app.post('/discussion/save_new_topic', params=params,
-                              extra_environ=dict(username='*anonymous'),
+                              extra_environ=dict(username=str('*anonymous')),
                               status=302)
             assert r.location.startswith(
                 'http://localhost/p/test/discussion/testforum/thread/'), r.location
@@ -551,11 +551,11 @@ class TestForum(TestController):
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'Test Thread'
         thread = self.app.post('/discussion/save_new_topic', params=params,
-                               extra_environ=dict(username='*anonymous')).follow()
+                               extra_environ=dict(username=str('*anonymous'))).follow()
 
         # assert post awaiting moderation
         r = self.app.get(thread.request.url,
-                         extra_environ=dict(username='*anonymous'))
+                         extra_environ=dict(username=str('*anonymous')))
         assert 'Post awaiting moderation' in r
         assert 'name="delete"' not in r
         assert 'name="approve"' not in r
@@ -572,9 +572,9 @@ class TestForum(TestController):
             if field.has_attr('name'):
                 params[field['name']] = field.get('value') or ''
         params[f.find('textarea')['name']] = 'anon reply to anon post content'
-        r = self.app.post(str(rep_url), params=params, extra_environ=dict(username='*anonymous'))
+        r = self.app.post(str(rep_url), params=params, extra_environ=dict(username=str('*anonymous')))
         r = self.app.get(thread.request.url,
-                         extra_environ=dict(username='*anonymous'))
+                         extra_environ=dict(username=str('*anonymous')))
         assert 'anon reply to anon post' not in r
         assert_equal(spam_checker.check.call_args[0][0], 'anon reply to anon post content')
 
@@ -606,7 +606,7 @@ class TestForum(TestController):
 
         # assert anon can't edit their original post
         r = self.app.get(thread.request.url,
-                    extra_environ=dict(username='*anonymous'))
+                    extra_environ=dict(username=str('*anonymous')))
         assert 'Post content' in r
         post_container = r.html.find('div', {'id': post.slug})
         btn_edit = post_container.find('a', {'title': 'Edit'})
@@ -959,7 +959,7 @@ class TestForum(TestController):
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'AAA'
         thread = self.app.post('/discussion/save_new_topic',
-                               params=params).follow(extra_environ=dict(username='*anonymous'))
+                               params=params).follow(extra_environ=dict(username=str('*anonymous')))
         thread_sidebar_menu = str(thread.html.find('div', {'id': 'sidebar'}))
         assert_not_in("flag_as_spam", thread_sidebar_menu)
 
@@ -985,7 +985,7 @@ class TestForum(TestController):
         form.submit()
         r = self.app.get('/admin/discussion/forums')
         assert 'téstforum'.encode('utf-8') in r
-        r = self.app.get('/p/test/discussion/create_topic/téstforum/'.encode('utf-8'))
+        r = self.app.get(h.urlquote('/p/test/discussion/create_topic/téstforum/'))
         assert '<option value="téstforum" selected>Tést Forum</option>' in r
 
     def test_create_topic_attachment(self):
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
index 1b9e54d..8f5a337 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
@@ -169,7 +169,7 @@ class TestForumAdmin(TestController):
         # forum can be viewed by member and non-member
         self.app.get('/discussion/secret')
         self.app.get('/discussion/secret',
-                     extra_environ=dict(username='test-user'))
+                     extra_environ=dict(username=str('test-user')))
         # make a post in the forum and confirm it is also viewable by member
         # and non-member
         r = self.app.get('/discussion/create_topic/')
@@ -187,12 +187,12 @@ class TestForumAdmin(TestController):
         r = self.app.post('/discussion/save_new_topic', params=params).follow()
         thread_url = r.request.url
         self.app.get(thread_url)
-        self.app.get(thread_url, extra_environ=dict(username='test-user'))
+        self.app.get(thread_url, extra_environ=dict(username=str('test-user')))
         # link shows up in app for member and non-member
         r = self.app.get('/discussion/')
         assert '/secret/' in r
         r = self.app.get('/discussion/',
-                         extra_environ=dict(username='test-user'))
+                         extra_environ=dict(username=str('test-user')))
         assert '/secret/' in r
         # make the forum member only viewable
         secret = FM.Forum.query.get(shortname='secret')
@@ -207,16 +207,16 @@ class TestForumAdmin(TestController):
         # member can see the forum, but non-member gets 403
         self.app.get('/discussion/secret')
         self.app.get('/discussion/secret',
-                     extra_environ=dict(username='test-user'), status=403)
+                     extra_environ=dict(username=str('test-user')), status=403)
         # member can see a thread in the forum, but non-member gets 403
         self.app.get(thread_url)
         self.app.get(thread_url,
-                     extra_environ=dict(username='test-user'), status=403)
+                     extra_environ=dict(username=str('test-user')), status=403)
         # link shows up in app for member but not non-member
         r = self.app.get('/discussion/')
         assert '/secret/' in r
         r = self.app.get('/discussion/',
-                         extra_environ=dict(username='test-user'))
+                         extra_environ=dict(username=str('test-user')))
         assert '/secret/' not in r
 
     def test_anon_posts(self):
@@ -239,7 +239,7 @@ class TestForumAdmin(TestController):
         params[f.find('select')['name']] = 'testforum'
         params[f.find('input', {'style': 'width: 90%'})['name']] = 'post topic'
         r = self.app.post('/discussion/save_new_topic',
-                          params=params, extra_environ=dict(username='*anonymous'))
+                          params=params, extra_environ=dict(username=str('*anonymous')))
         assert r.location == 'http://localhost/auth/'
         # allow anon posts in the forum
         testforum = FM.Forum.query.get(shortname='testforum')
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py b/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
index 7b6a9bc..bca2e08 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_rest.py
@@ -222,16 +222,16 @@ class TestRootRestController(TestDiscussionApiBase):
         acl.append(auth_read)
         self.api_get('/rest/p/test/discussion/')
         self.app.get('/rest/p/test/discussion/',
-                     extra_environ={'username': '*anonymous'},
+                     extra_environ={'username': str('*anonymous')},
                      status=401)
         self.api_get('/rest/p/test/discussion/general/')
         self.app.get('/rest/p/test/discussion/general/',
-                     extra_environ={'username': '*anonymous'},
+                     extra_environ={'username': str('*anonymous')},
                      status=401)
         t = ForumThread.query.find({'subject': 'Hi guys'}).first()
         self.api_get('/rest/p/test/discussion/general/thread/%s/' % t._id)
         self.app.get('/rest/p/test/discussion/general/thread/%s/' % t._id,
-                     extra_environ={'username': '*anonymous'},
+                     extra_environ={'username': str('*anonymous')},
                      status=401)
 
     def test_private_forums(self):
@@ -245,7 +245,7 @@ class TestRootRestController(TestDiscussionApiBase):
         r = self.api_get('/rest/p/test/discussion/')
         assert_equal(len(r.json['forums']), 2)
         r = self.app.get('/rest/p/test/discussion/',
-                         extra_environ={'username': '*anonymous'})
+                         extra_environ={'username': str('*anonymous')})
         assert_equal(len(r.json['forums']), 1)
         assert_equal(r.json['forums'][0]['shortname'], 'general')
 
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index 3c18bcd..88c0338 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -479,7 +479,7 @@ class TestRootController(_TestCase):
         assert_in('refresh queued', r)
         assert_equal(1, M.MonQTask.query.find(dict(task_name='allura.tasks.repo_tasks.refresh')).count())
 
-        r = self.app.get('/p/test/src-git/refresh', extra_environ={'HTTP_REFERER': '/p/test/src-git/'}, status=302)
+        r = self.app.get('/p/test/src-git/refresh', extra_environ={'HTTP_REFERER': str('/p/test/src-git/')}, status=302)
         assert_in('is being refreshed', self.webflash(r))
         assert_equal(2, M.MonQTask.query.find(dict(task_name='allura.tasks.repo_tasks.refresh')).count())
 
@@ -634,7 +634,7 @@ class TestFork(_TestCase):
 
     def test_merge_request_invisible_to_non_admin(self):
         assert 'Request Merge' not in self._fork_page(
-            extra_environ=dict(username='test-user'))
+            extra_environ=dict(username=str('test-user')))
 
     def test_merge_action_available_to_admin(self):
         self.app.get('/p/test2/code/request_merge')
@@ -642,7 +642,7 @@ class TestFork(_TestCase):
     def test_merge_action_unavailable_to_non_admin(self):
         self.app.get(
             '/p/test2/code/request_merge',
-            status=403, extra_environ=dict(username='test-user'))
+            status=403, extra_environ=dict(username=str('test-user')))
 
     def test_merge_request_detail_view(self):
         r, mr_num = self._request_merge()
@@ -798,7 +798,7 @@ class TestFork(_TestCase):
                               'summary': 'changed summary',
                               'description': 'changed description'
                           },
-                          extra_environ=dict(username='*anonymous'),
+                          extra_environ=dict(username=str('*anonymous')),
                           status=302,
                           ).follow()
         assert 'Login' in r
@@ -869,7 +869,7 @@ class TestFork(_TestCase):
             '/p/test/src-git/merge-requests/1/update_markdown',
             params={
                 'text': '- [x] checkbox'},
-            extra_environ=dict(username='*anonymous'))
+            extra_environ=dict(username=str('*anonymous')))
         assert response.json['status'] == 'no_permission'
 
     @patch.object(GM.Repository, 'merge_request_commits', autospec=True)
diff --git a/ForgeImporters/forgeimporters/tests/github/functional/test_github.py b/ForgeImporters/forgeimporters/tests/github/functional/test_github.py
index bfada9e..e1fa14a 100644
--- a/ForgeImporters/forgeimporters/tests/github/functional/test_github.py
+++ b/ForgeImporters/forgeimporters/tests/github/functional/test_github.py
@@ -37,12 +37,12 @@ class TestGitHubImportController(TestController, TestCase):
 
     def test_login_overlay(self):
         r = self.app.get('/p/import_project/github/',
-                         extra_environ=dict(username='*anonymous'))
+                         extra_environ=dict(username=str('*anonymous')))
         self.assertIn('GitHub Project Importer', r)
         self.assertIn('Login Required', r)
 
         r = self.app.post('/p/import_project/github/process',
-                          extra_environ=dict(username='*anonymous'), status=302)
+                          extra_environ=dict(username=str('*anonymous')), status=302)
         self.assertIn('/auth/', r.location)
 
 
diff --git a/ForgeImporters/forgeimporters/trac/tests/functional/test_trac.py b/ForgeImporters/forgeimporters/trac/tests/functional/test_trac.py
index c8237cd..e9ce596 100644
--- a/ForgeImporters/forgeimporters/trac/tests/functional/test_trac.py
+++ b/ForgeImporters/forgeimporters/trac/tests/functional/test_trac.py
@@ -53,6 +53,6 @@ class TestTracImportController(TestController):
 
 
     def test_import_with_phone_validation(self):
-        self.app.extra_environ = {'username': 'test-user'}
+        self.app.extra_environ = {'username': str('test-user')}
         with h.push_config(config, **{'project.verify_phone': 'true'}):
             self.test_submit()
\ No newline at end of file
diff --git a/ForgeLink/forgelink/link_main.py b/ForgeLink/forgelink/link_main.py
index 5e4af9d..acc13df 100644
--- a/ForgeLink/forgelink/link_main.py
+++ b/ForgeLink/forgelink/link_main.py
@@ -129,6 +129,8 @@ class RootController(BaseController):
         path = "/".join(remainder)
         url = c.app.config.options.get('url')
         if url:
+            # h.urlquote is better than utf8 encoding for Location headers, but in this case the url can be a full
+            # http://... url and we don't want to urlquote/urlencode that part
             redirect(url + h.really_unicode(path).encode('utf-8'))
         return dict()
 
diff --git a/ForgeLink/forgelink/tests/functional/test_rest.py b/ForgeLink/forgelink/tests/functional/test_rest.py
index 9678042..ced7978 100644
--- a/ForgeLink/forgelink/tests/functional/test_rest.py
+++ b/ForgeLink/forgelink/tests/functional/test_rest.py
@@ -54,19 +54,19 @@ class TestLinkApi(TestRestApiBase):
 
     def test_rest_link_get_permissions(self):
         self.app.get('/rest/p/test/link',
-                     extra_environ={'username': '*anonymous'}, status=200)
+                     extra_environ={'username': str('*anonymous')}, status=200)
         p = M.Project.query.get(shortname='test')
         acl = p.app_instance('link').config.acl
         anon = M.ProjectRole.by_name('*anonymous')._id
         anon_read = M.ACE.allow(anon, 'read')
         acl.remove(anon_read)
         self.app.get('/rest/p/test/link',
-                     extra_environ={'username': '*anonymous'}, status=401)
+                     extra_environ={'username': str('*anonymous')}, status=401)
 
     def test_rest_link_post_permissions(self):
         self.app.post('/rest/p/test/link',
                       params={'url': 'http://yahoo.com'},
-                      extra_environ={'username': '*anonymous'},
+                      extra_environ={'username': str('*anonymous')},
                       status=401)
         p = M.Project.query.get(shortname='test')
         acl = p.app_instance('link').config.acl
@@ -75,7 +75,7 @@ class TestLinkApi(TestRestApiBase):
         acl.append(anon_configure)
         self.app.post('/rest/p/test/link',
                       params={'url': 'http://yahoo.com'},
-                      extra_environ={'username': '*anonymous'},
+                      extra_environ={'username': str('*anonymous')},
                       status=200)
         r = self.api_get('/rest/p/test/link'.encode('utf-8'))
         assert_equal(r.json['url'], 'http://yahoo.com')
diff --git a/ForgeShortUrl/forgeshorturl/tests/functional/test.py b/ForgeShortUrl/forgeshorturl/tests/functional/test.py
index 059a348..a93ab53 100644
--- a/ForgeShortUrl/forgeshorturl/tests/functional/test.py
+++ b/ForgeShortUrl/forgeshorturl/tests/functional/test.py
@@ -91,7 +91,7 @@ class TestRootController(TestController):
         assert 'http://www.amazone.com/' in r
         assert '<td><small>yes</small></td>' in r
         self.app.get('/url/test_private',
-                     extra_environ=dict(username='*anonymous'),
+                     extra_environ=dict(username=str('*anonymous')),
                      status=404)
         self.app.get('/url/test_private',
                      status=302)
@@ -133,9 +133,9 @@ class TestRootController(TestController):
         self.app.post('/admin/url/add',
                       params=dict(short_url='g',
                                   full_url='http://google.com/'),
-                      extra_environ=dict(username='test-user'), status=403)
+                      extra_environ=dict(username=str('test-user')), status=403)
         self.app.post('/admin/url/remove', params=dict(shorturl='g'),
-                      extra_environ=dict(username='test-user'), status=403)
+                      extra_environ=dict(username=str('test-user')), status=403)
 
     def test_build_short_url(self):
         with h.push_config(config, **{
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index ede3637..75256f2 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -246,7 +246,7 @@ class TestSubprojectTrackerController(TrackerTestController):
         """Test that non-admin users can see tickets created by admins."""
         self.new_ticket(summary="my ticket", mount_point="/sub1/tickets/")
         response = self.app.get('/p/test/sub1/tickets/',
-                                extra_environ=dict(username='*anonymous'))
+                                extra_environ=dict(username=str('*anonymous')))
         assert 'my ticket' in response
 
     @td.with_tool('test/sub1', 'Tickets', 'tickets')
@@ -257,7 +257,7 @@ class TestSubprojectTrackerController(TrackerTestController):
         M.MonQTask.run_ready()
         ThreadLocalORMSession.flush_all()
         response = self.app.get('/p/test/sub1/tickets/search/?q=my',
-                                extra_environ=dict(username='*anonymous'))
+                                extra_environ=dict(username=str('*anonymous')))
         assert 'my ticket' in response, response.showbrowser()
 
     @td.with_tool('test/sub1', 'Tickets', 'tickets')
@@ -319,7 +319,7 @@ class TestFunctionalController(TrackerTestController):
             '/bugs/1/update_markdown',
             params={
                 'text': '- [x] checkbox'},
-            extra_environ=dict(username='*anonymous'))
+            extra_environ=dict(username=str('*anonymous')))
         assert response.json['status'] == 'no_permission'
 
     def test_labels(self):
@@ -348,7 +348,7 @@ class TestFunctionalController(TrackerTestController):
         # Private tickets shouldn't be included in counts if user doesn't
         # have read access to private tickets.
         r = self.app.get('/bugs/milestone_counts',
-                         extra_environ=dict(username='*anonymous'))
+                         extra_environ=dict(username=str('*anonymous')))
         counts['milestone_counts'][0]['count'] = 1
         assert_equal(r.body, json.dumps(counts))
 
@@ -371,7 +371,7 @@ class TestFunctionalController(TrackerTestController):
         
         # Private tickets shouldn't be included in counts if user doesn't
         # have read access to private tickets.
-        r = self.app.get('/bugs/bin_counts', extra_environ=dict(username='*anonymous'))
+        r = self.app.get('/bugs/bin_counts', extra_environ=dict(username=str('*anonymous')))
         assert_equal(r.json, {"bin_counts": [{"count": 1, "label": "Changes"},
                                              {"count": 0, "label": "Closed Tickets"},
                                              {"count": 1, "label": "Open Tickets"}]})
@@ -387,7 +387,7 @@ class TestFunctionalController(TrackerTestController):
         # Private tickets shouldn't be included in counts if user doesn't
         # have read access to private tickets.
         r = self.app.get('/bugs/milestone/1.0/',
-                         extra_environ=dict(username='*anonymous'))
+                         extra_environ=dict(username=str('*anonymous')))
         assert '0 / 1' in r
 
     def test_new_ticket_form(self):
@@ -658,7 +658,7 @@ class TestFunctionalController(TrackerTestController):
         assert '2 results' in search_response
         assert 'Private Ticket' in search_response
         # Unauthorized user doesn't see private ticket on list page...
-        env = dict(username='*anonymous')
+        env = dict(username=str('*anonymous'))
         r = self.app.get('/p/test/bugs/', extra_environ=env)
         assert '1 results' in r
         assert 'Private Ticket' not in r
@@ -718,7 +718,7 @@ class TestFunctionalController(TrackerTestController):
         assert_in('edit_post_form reply', response)  # Make sure admin can still comment
 
         # Unauthorized user cannot comment or even see form fields
-        env = dict(username='*anonymous')
+        env = dict(username=str('*anonymous'))
         r = self.app.get('/p/test/bugs/1', extra_environ=env)
         assert_not_in('edit_post_form reply', r)
 
@@ -782,7 +782,7 @@ class TestFunctionalController(TrackerTestController):
         assert 'Create Ticket' in index_view
 
         # Make sure the 'Create Ticket' button is disabled for user without 'create' perm
-        r = self.app.get('/bugs/', extra_environ=dict(username='*anonymous'))
+        r = self.app.get('/bugs/', extra_environ=dict(username=str('*anonymous')))
         create_button = r.html.find('a', attrs={'href': '/p/test/bugs/new/'})
         assert_equal(create_button['class'], ['icon', 'sidebar-disabled'])
 
@@ -1416,11 +1416,11 @@ class TestFunctionalController(TrackerTestController):
         M.MonQTask.run_ready()
         ThreadLocalORMSession.flush_all()
         response = self.app.get('/p/test/bugs/search/?q=reported_by_s:$USER',
-                                extra_environ={'username': 'test-user-0'})
+                                extra_environ={'username': str('test-user-0')})
         assert '1 result' in response, response.showbrowser()
         assert 'test first ticket' in response, response.showbrowser()
         response = self.app.get('/p/test/bugs/search/?q=reported_by_s:$USER',
-                                extra_environ={'username': 'test-user-1'})
+                                extra_environ={'username': str('test-user-1')})
         assert '1 result' in response, response.showbrowser()
         assert 'test second ticket' in response, response.showbrowser()
 
@@ -1959,7 +1959,7 @@ class TestFunctionalController(TrackerTestController):
         assert_true(r.html.find('div', {'id': 'vote'}))
 
         # test vote form not visible to anon user
-        r = self.app.get('/bugs/1/', extra_environ=dict(username='*anonymous'))
+        r = self.app.get('/bugs/1/', extra_environ=dict(username=str('*anonymous')))
         assert_false(r.html.find('div', {'id': 'vote'}))
 
         r = self.app.get('/bugs/1/')
@@ -1980,7 +1980,7 @@ class TestFunctionalController(TrackerTestController):
 
         # vote down by another user
         r = self.app.post('/bugs/1/vote', dict(vote='d'),
-                          extra_environ=dict(username='test-user-0'))
+                          extra_environ=dict(username=str('test-user-0')))
 
         expected_resp = json.dumps(dict(status='ok', votes_up=1, votes_down=1, votes_percent=50))
         assert r.response.content == expected_resp
@@ -2007,15 +2007,15 @@ class TestFunctionalController(TrackerTestController):
         but can't edit it without `update` permission.
         """
         response = self.app.get('/p/test/tracker/',
-                                extra_environ=dict(username='test-user-0'))
+                                extra_environ=dict(username=str('test-user-0')))
         assert 'Create Ticket' in response
 
         response = self.new_ticket(summary='test create, not update',
                                    mount_point='/tracker/',
-                                   extra_environ=dict(username='test-user-0'))
+                                   extra_environ=dict(username=str('test-user-0')))
         ticket_url = response.headers['Location']
         response = self.app.get(ticket_url,
-                                extra_environ=dict(username='test-user-0'))
+                                extra_environ=dict(username=str('test-user-0')))
         assert not response.html.find('div', {'class': 'error'})
         assert not response.html.find('a', {'class': 'edit_ticket'})
 
@@ -2023,13 +2023,13 @@ class TestFunctionalController(TrackerTestController):
                   post_install_hook=post_install_update_ticket_permission)
     def test_update_permission(self):
         r = self.app.get('/p/test/tracker/',
-                         extra_environ=dict(username='*anonymous'))
+                         extra_environ=dict(username=str('*anonymous')))
         assert 'Create Ticket' in r
 
         r = self.new_ticket(summary='test', mount_point='/tracker/',
-                            extra_environ=dict(username='*anonymous'))
+                            extra_environ=dict(username=str('*anonymous')))
         ticket_url = r.headers['Location']
-        r = self.app.get(ticket_url, extra_environ=dict(username='*anonymous'))
+        r = self.app.get(ticket_url, extra_environ=dict(username=str('*anonymous')))
         a = r.html.find('a', {'class': 'icon edit_ticket'})
         assert_equal(a.text, '\xa0Edit')
 
@@ -2045,7 +2045,7 @@ class TestFunctionalController(TrackerTestController):
         if update_permission in acl:
             acl.remove(update_permission)
         # test-user creates private ticket
-        env = {'username': 'test-user'}
+        env = {'username': str('test-user')}
         post_data = {
             'ticket_form.summary': 'Private ticket title',
             'ticket_form.private': True
@@ -2088,12 +2088,12 @@ class TestFunctionalController(TrackerTestController):
     def test_ticket_delete_without_permission(self):
         self.new_ticket(summary='Test ticket')
         self.app.post('/bugs/1/delete',
-                      extra_environ=dict(username='*anonymous'))
+                      extra_environ=dict(username=str('*anonymous')))
         r = self.app.get('/bugs/')
         assert '<a href="/p/test/bugs/1/">Test ticket</a>' in r
         self.app.post('/bugs/1/delete')
         self.app.post('/bugs/1/undelete',
-                      extra_environ=dict(username='*anonymous'))
+                      extra_environ=dict(username=str('*anonymous')))
         r = self.app.get('/bugs/')
         assert 'No open tickets found.' in r
 
@@ -2103,14 +2103,14 @@ class TestFunctionalController(TrackerTestController):
         r = self.app.get('/p/test/bugs/1/')
         assert '#1 test' in r
         self.app.get('/p/test/bugs/1/',
-                     extra_environ=dict(username='*anonymous'), status=404)
+                     extra_environ=dict(username=str('*anonymous')), status=404)
         r = self.app.get('/p/test/bugs/',
                          params=dict(q='test', deleted='True'))
         assert '<td><a href="/p/test/bugs/1/">test' in r
         assert '<tr class=" deleted">' in r
         r = self.app.get(
             '/p/test/bugs/', params=dict(q='test', deleted='True'),
-            extra_environ=dict(username='*anonymous'))
+            extra_environ=dict(username=str('*anonymous')))
         assert 'No open tickets found.' in r
 
     def test_show_hide_deleted_tickets(self):
@@ -2208,26 +2208,26 @@ class TestFunctionalController(TrackerTestController):
 
     def test_move_ticket_bad_data(self):
         self.new_ticket(summary='test')
-        r = self.app.post('/p/test/bugs/1/move', extra_environ={'HTTP_REFERER': '/p/test/bugs/1/'}).follow()  # empty POST
+        r = self.app.post('/p/test/bugs/1/move', extra_environ={'HTTP_REFERER': str('/p/test/bugs/1/')}).follow()  # empty POST
         assert 'Select valid tracker' in r, r
         r = self.app.post('/p/test/bugs/1/move',
                           params={'tracker': 'invalid tracker id'},
-                          extra_environ={'HTTP_REFERER': '/p/test/bugs/1/'}).follow()
+                          extra_environ={'HTTP_REFERER': str('/p/test/bugs/1/')}).follow()
         assert 'Select valid tracker' in r, r
         p = M.Project.query.get(shortname='test')
         tracker = p.app_instance('bugs')
         r = self.app.post('/p/test/bugs/1/move',
                           params={'tracker': str(tracker.config._id)},
-                          extra_environ={'HTTP_REFERER': '/p/test/bugs/1/'}).follow()
+                          extra_environ={'HTTP_REFERER': str('/p/test/bugs/1/')}).follow()
         assert 'Ticket already in a selected tracker' in r, r
 
     def test_move_ticket_access(self):
         self.new_ticket(summary='test')
         self.app.get('/p/test/bugs/1/move',
-                     extra_environ={'username': 'test-user'},
+                     extra_environ={'username': str('test-user')},
                      status=403)
         self.app.post('/p/test/bugs/1/move',
-                      extra_environ={'username': 'test-user'},
+                      extra_environ={'username': str('test-user')},
                       status=403)
 
     @td.with_tool('test', 'Tickets', 'dummy')
@@ -2319,7 +2319,7 @@ class TestFunctionalController(TrackerTestController):
 
         # subscribe test-user to ticket #2
         self.app.post('/p/test/bugs/2/subscribe', {'subscribe': True},
-                      extra_environ={'username': 'test-user'})
+                      extra_environ={'username': str('test-user')})
         assert M.Mailbox.query.get(user_id=user._id,
                                    project_id=p._id,
                                    app_config_id=bugs.config._id,
@@ -2558,7 +2558,7 @@ class TestFunctionalController(TrackerTestController):
         Credentials.get().clear()
         # make a ticket created by & assigned to test-user
         self.new_ticket(summary='foo bar', assigned_to='test-user', private=True,
-                        extra_environ={'username': 'test-user'})
+                        extra_environ={'username': str('test-user')})
         # but then remove the user
         M.User.query.remove({'username': 'test-user'})
 
@@ -2737,7 +2737,7 @@ class TestEmailMonitoring(TrackerTestController):
         self.app.post('/doc-bugs/1/update_ticket', {
             'summary': 'test moderation',
             'comment': 'test unmoderated post'
-        }, extra_environ=dict(username='*anonymous'))
+        }, extra_environ=dict(username=str('*anonymous')))
         send_direct.assert_called_with(
             str(M.User.query.get(username='test-admin')._id))
 
@@ -2751,7 +2751,7 @@ class TestEmailMonitoring(TrackerTestController):
         self.app.post('/doc-bugs/1/update_ticket', {
             'summary': 'test moderation',
             'comment': 'test unmoderated post'
-        }, extra_environ=dict(username='*anonymous'))
+        }, extra_environ=dict(username=str('*anonymous')))
         assert not send_direct.called
 
     @patch('forgetracker.model.ticket.Notification.send_simple')
@@ -2983,13 +2983,13 @@ class TestBulkMove(TrackerTestController):
     def test_access_restriction(self):
         self.app.get('/bugs/move/', status=200)
         self.app.get('/bugs/move/',
-                     extra_environ={'username': 'test-user-0'},
+                     extra_environ={'username': str('test-user-0')},
                      status=403)
         self.app.get('/bugs/move/',
-                     extra_environ={'username': '*anonymous'},
+                     extra_environ={'username': str('*anonymous')},
                      status=302)
         self.app.post('/bugs/move_tickets',
-                      extra_environ={'username': 'test-user-0'},
+                      extra_environ={'username': str('test-user-0')},
                       status=403)
 
     def test_ticket_list(self):
diff --git a/ForgeTracker/forgetracker/tests/unit/test_milestone_controller.py b/ForgeTracker/forgetracker/tests/unit/test_milestone_controller.py
index 579b805..00d2382 100644
--- a/ForgeTracker/forgetracker/tests/unit/test_milestone_controller.py
+++ b/ForgeTracker/forgetracker/tests/unit/test_milestone_controller.py
@@ -40,7 +40,7 @@ def test_unicode_lookup():
         root = None
         field = 'milestone'
         # u'Перспектива'
-        milestone_urlparam = '%D0%9F%D0%B5%D1%80%D1%81%D0%BF%D0%B5%D0%BA%D1%82%D0%B8%D0%B2%D0%B0'
+        milestone_urlparam = str('%D0%9F%D0%B5%D1%80%D1%81%D0%BF%D0%B5%D0%BA%D1%82%D0%B8%D0%B2%D0%B0')
         mc = MilestoneController(root, field, milestone_urlparam)
 
     assert mc.milestone  # check that it is found
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index 02cfbaf..50c5db8 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -855,11 +855,11 @@ class RootController(BaseController, FeedController):
             q = query
         result = TM.Ticket.paged_search(
             c.app.config, c.user, q, page=page, sort=sort, show_deleted=deleted, **kw)
-        response.headers['Content-Type'] = ''
-        response.content_type = 'application/xml'
+        response.headers['Content-Type'] = str('')
+        response.content_type = str('application/xml')
         d = dict(title='Ticket search results', link=h.absurl(c.app.url),
                  description='You searched for %s' % q, language='en')
-        if request.environ['PATH_INFO'].endswith('.atom'):
+        if request.environ['PATH_INFO'].endswith(str('.atom')):
             feed = FG.Atom1Feed(**d)
         else:
             feed = FG.Rss201rev2Feed(**d)
diff --git a/ForgeWiki/forgewiki/tests/functional/test_root.py b/ForgeWiki/forgewiki/tests/functional/test_root.py
index f2fd42c..871aabc 100644
--- a/ForgeWiki/forgewiki/tests/functional/test_root.py
+++ b/ForgeWiki/forgewiki/tests/functional/test_root.py
@@ -59,7 +59,7 @@ class TestRootController(TestController):
         assert 'Create Page' in r
         # No 'Create Page' button if user doesn't have 'create' perm
         r = self.app.get('/wiki/Home',
-                         extra_environ=dict(username='*anonymous'))
+                         extra_environ=dict(username=str('*anonymous')))
         assert 'Create Page' not in r, r
 
     def test_create_wiki_page(self):
@@ -82,7 +82,7 @@ class TestRootController(TestController):
 
     def test_root_new_page(self):
         response = self.app.get('/wiki/new_page?title=' + h.urlquote('tést'))
-        assert 'tést' in response
+        assert_equal(response.location, 'http://localhost/wiki/t%C3%A9st/')
 
     def test_root_new_search(self):
         self.app.get(h.urlquote('/wiki/tést/'))
@@ -151,17 +151,17 @@ class TestRootController(TestController):
         assert_in('To search for an exact phrase', div.text)
 
     def test_nonexistent_page_edit(self):
-        resp = self.app.get('/wiki/tést/')
+        resp = self.app.get(h.urlquote('/wiki/tést/'))
         assert resp.location.endswith(h.urlquote('/wiki/tést/edit')), resp.location
         resp = resp.follow()
         assert 'tést' in resp
 
     def test_nonexistent_page_noedit(self):
-        self.app.get('/wiki/tést/',
-                     extra_environ=dict(username='*anonymous'),
+        self.app.get(h.urlquote('/wiki/tést/'),
+                     extra_environ=dict(username=str('*anonymous')),
                      status=404)
-        self.app.get('/wiki/tést/',
-                     extra_environ=dict(username='test-user'),
+        self.app.get(h.urlquote('/wiki/tést/'),
+                     extra_environ=dict(username=str('test-user')),
                      status=404)
 
     @patch('forgewiki.wiki_main.g.director.create_activity')
@@ -215,34 +215,34 @@ class TestRootController(TestController):
         assert 'page.dot' in r
 
     def test_subpage_attempt(self):
-        self.app.get('/wiki/tést/')
+        self.app.get(h.urlquote('/wiki/tést/'))
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'text1',
                 'labels': '',
                 })
-        assert '/p/test/wiki/Home/' in self.app.get('/wiki/tést/Home/')
-        self.app.get('/wiki/tést/notthere/', status=404)
+        assert '/p/test/wiki/Home/' in self.app.get(h.urlquote('/wiki/tést/Home/'))
+        self.app.get(h.urlquote('/wiki/tést/notthere/'), status=404)
 
     def test_page_history(self):
-        self.app.get('/wiki/tést/')
+        self.app.get(h.urlquote('/wiki/tést/'))
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'text1',
                 'labels': '',
                 })
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'text2',
                 'labels': '',
                 })
-        response = self.app.get('/wiki/tést/history')
+        response = self.app.get(h.urlquote('/wiki/tést/history'))
         assert 'tést' in response
         # two revisions are shown
         assert '2 by Test Admin' in response
@@ -250,8 +250,8 @@ class TestRootController(TestController):
         # you can revert to an old revison, but not the current one
         assert response.html.find('a', {'data-dialog-id': '1'}), response.html
         assert not response.html.find('a', {'data-dialog-id': '2'})
-        response = self.app.get('/wiki/tést/history',
-                                extra_environ=dict(username='*anonymous'))
+        response = self.app.get(h.urlquote('/wiki/tést/history'),
+                                extra_environ=dict(username=str('*anonymous')))
         # two revisions are shown
         assert '2 by Test Admin' in response
         assert '1 by Test Admin' in response
@@ -260,20 +260,20 @@ class TestRootController(TestController):
         assert not response.html.find('a', {'data-dialog-id': '2'})
 
         # view an older version
-        response = self.app.get('/wiki/tést/?version=1')
+        response = self.app.get(h.urlquote('/wiki/tést/') + '?version=1')
         response.mustcontain('text1')
         response.mustcontain(no='text2')
 
     def test_page_diff(self):
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'sometext',
                 'labels': '',
                 })
-        self.app.post('/wiki/tést/revert', params=dict(version='1'))
-        response = self.app.get('/wiki/tést/diff?v1=0&v2=0')
+        self.app.post(h.urlquote('/wiki/tést/revert'), params=dict(version='1'))
+        response = self.app.get(h.urlquote('/wiki/tést/diff') + '?v1=0&v2=0')
         assert 'tést' in response
         d = dict(title='testdiff', text="""**Optionally**, you may also want to remove all the unused accounts that have accumulated (one was created for *every* logged in SF-user who has visited your MediaWiki hosted app):
 
@@ -371,104 +371,105 @@ class TestRootController(TestController):
 
     def test_page_revert_no_text(self):
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': '',
                 'labels': '',
                 })
-        response = self.app.post('/wiki/tést/revert', params=dict(version='1'))
+        response = self.app.post(h.urlquote('/wiki/tést/revert'), params=dict(version='1'))
         assert '.' in response.json['location']
-        response = self.app.get('/wiki/tést/')
+        response = self.app.get(h.urlquote('/wiki/tést/'))
         assert 'tést' in response
 
     def test_page_revert_with_text(self):
-        self.app.get('/wiki/tést/')
+        self.app.get(h.urlquote('/wiki/tést/'))
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'sometext',
                 'labels': '',
                 })
-        response = self.app.post('/wiki/tést/revert', params=dict(version='1'))
+        response = self.app.post(h.urlquote('/wiki/tést/revert'), params=dict(version='1'))
         assert '.' in response.json['location']
-        response = self.app.get('/wiki/tést/')
+        response = self.app.get(h.urlquote('/wiki/tést/'))
         assert 'tést' in response
 
     @patch('forgewiki.wiki_main.g.spam_checker')
     def test_page_update(self, spam_checker):
-        self.app.get('/wiki/tést/')
+        self.app.get(h.urlquote('/wiki/tést/'))
         response = self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'sometext',
                 'labels': '',
                 })
         assert_equal(spam_checker.check.call_args[0][0], 'tést\nsometext')
-        assert 'tést' in response
+        assert_equal(response.location, 'http://localhost/wiki/t%C3%A9st/')
 
     def test_page_get_markdown(self):
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': '- [ ] checkbox',
                 'labels': '',
                 })
-        response = self.app.get('/wiki/tést/get_markdown')
+        response = self.app.get(h.urlquote('/wiki/tést/get_markdown'))
         assert '- [ ] checkbox' in response
 
 
     def test_page_update_markdown(self):
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': '- [ ] checkbox',
                 'labels': '',
                 })
         response = self.app.post(
-            '/wiki/tést/update_markdown',
+            h.urlquote('/wiki/tést/update_markdown'),
             params={
                 'text': '- [x] checkbox'})
+        print(response)
         assert response.json['status'] == 'success'
         # anon users can't edit markdown
         response = self.app.post(
-            '/wiki/tést/update_markdown',
+            h.urlquote('/wiki/tést/update_markdown'),
             params={
                 'text': '- [x] checkbox'},
-            extra_environ=dict(username='*anonymous'))
+            extra_environ=dict(username=str('*anonymous')))
         assert response.json['status'] == 'no_permission'
 
     def test_page_label_unlabel(self):
-        self.app.get('/wiki/tést/')
+        self.app.get(h.urlquote('/wiki/tést/'))
         response = self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'sometext',
                 'labels': 'yellow,green',
                 })
-        assert 'tést' in response
+        assert_equal(response.location, 'http://localhost/wiki/t%C3%A9st/')
         response = self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'sometext',
                 'labels': 'yellow',
                 })
-        assert 'tést' in response
+        assert_equal(response.location, 'http://localhost/wiki/t%C3%A9st/')
 
     def test_page_label_count(self):
         labels = "label"
         for i in range(1, 100):
             labels += ',label%s' % i
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'sometext',
                 'labels': labels,
                 })
@@ -484,46 +485,46 @@ class TestRootController(TestController):
 
     def test_new_attachment(self):
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'sometext',
                 'labels': '',
                 })
         content = file(__file__).read()
-        self.app.post('/wiki/tést/attach',
+        self.app.post(h.urlquote('/wiki/tést/attach'),
                       upload_files=[('file_info', 'test_root.py', content)])
-        response = self.app.get('/wiki/tést/')
+        response = self.app.get(h.urlquote('/wiki/tést/'))
         assert 'test_root.py' in response
 
     def test_attach_two_files(self):
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'sometext',
                 'labels': '',
                 })
         content = file(__file__).read()
-        self.app.post('/wiki/tést/attach',
+        self.app.post(h.urlquote('/wiki/tést/attach'),
                       upload_files=[('file_info', 'test1.py', content), ('file_info', 'test2.py', content)])
-        response = self.app.get('/wiki/tést/')
+        response = self.app.get(h.urlquote('/wiki/tést/'))
         assert 'test1.py' in response
         assert 'test2.py' in response
 
     def test_new_text_attachment_content(self):
         self.app.post(
-            '/wiki/tést/update',
+            h.urlquote('/wiki/tést/update'),
             params={
-                'title': 'tést',
+                'title': 'tést'.encode('utf-8'),
                 'text': 'sometext',
                 'labels': '',
                 })
         file_name = 'test_root.py'
         file_data = file(__file__).read()
         upload = ('file_info', file_name, file_data)
-        self.app.post('/wiki/tést/attach', upload_files=[upload])
-        page_editor = self.app.get('/wiki/tést/edit')
+        self.app.post(h.urlquote('/wiki/tést/attach'), upload_files=[upload])
+        page_editor = self.app.get(h.urlquote('/wiki/tést/edit'))
         download = page_editor.click(description=file_name)
         assert_true(download.body == file_data)
 
@@ -559,7 +560,7 @@ class TestRootController(TestController):
                 filename) not in img_srcs, img_srcs
 
     def test_sidebar_static_page(self):
-        response = self.app.get('/wiki/tést/')
+        response = self.app.get(h.urlquote('/wiki/tést/'))
         assert 'Edit this page' not in response
         assert 'Related Pages' not in response
 
@@ -599,12 +600,12 @@ class TestRootController(TestController):
         assert 'bbb' in response
 
     def test_show_discussion(self):
-        self.app.post('/wiki/tést/update', params={
-            'title': 'tést',
+        self.app.post(h.urlquote('/wiki/tést/update'), params={
+            'title': 'tést'.encode('utf-8'),
             'text': 'sometext',
             'labels': '',
             })
-        wiki_page = self.app.get('/wiki/tést/')
+        wiki_page = self.app.get(h.urlquote('/wiki/tést/'))
         assert wiki_page.html.find('div', {'id': 'new_post_holder'})
         options_admin = self.app.get(
             '/admin/wiki/options', validate_chunk=True)
@@ -614,16 +615,16 @@ class TestRootController(TestController):
         options_admin2 = self.app.get(
             '/admin/wiki/options', validate_chunk=True)
         assert not options_admin2.form['show_discussion'].checked
-        wiki_page2 = self.app.get('/wiki/tést/')
+        wiki_page2 = self.app.get(h.urlquote('/wiki/tést/'))
         assert not wiki_page2.html.find('div', {'id': 'new_post_holder'})
 
     def test_show_left_bar(self):
-        self.app.post('/wiki/tést/update', params={
-            'title': 'tést',
+        self.app.post(h.urlquote('/wiki/tést/update'), params={
+            'title': 'tést'.encode('utf-8'),
             'text': 'sometext',
             'labels': '',
             })
-        wiki_page = self.app.get('/wiki/tést/')
+        wiki_page = self.app.get(h.urlquote('/wiki/tést/'))
         assert wiki_page.html.find('ul', {'class': 'sidebarmenu'})
         options_admin = self.app.get(
             '/admin/wiki/options', validate_chunk=True)
@@ -634,18 +635,18 @@ class TestRootController(TestController):
             '/admin/wiki/options', validate_chunk=True)
         assert not options_admin2.form['show_left_bar'].checked
         wiki_page2 = self.app.get(
-            '/wiki/tést/', extra_environ=dict(username='*anonymous'))
+            h.urlquote('/wiki/tést/'), extra_environ=dict(username=str('*anonymous')))
         assert not wiki_page2.html.find('ul', {'class': 'sidebarmenu'})
-        wiki_page3 = self.app.get('/wiki/tést/')
+        wiki_page3 = self.app.get(h.urlquote('/wiki/tést/'))
         assert not wiki_page3.html.find('ul', {'class': 'sidebarmenu'})
 
     def test_show_metadata(self):
-        self.app.post('/wiki/tést/update', params={
-            'title': 'tést',
+        self.app.post(h.urlquote('/wiki/tést/update'), params={
+            'title': 'tést'.encode('utf-8'),
             'text': 'sometext',
             'labels': '',
             })
-        wiki_page = self.app.get('/wiki/tést/')
+        wiki_page = self.app.get(h.urlquote('/wiki/tést/'))
         assert wiki_page.html.find('div', {'class': 'editbox'})
         options_admin = self.app.get(
             '/admin/wiki/options', validate_chunk=True)
@@ -655,12 +656,12 @@ class TestRootController(TestController):
         options_admin2 = self.app.get(
             '/admin/wiki/options', validate_chunk=True)
         assert not options_admin2.form['show_right_bar'].checked
-        wiki_page2 = self.app.get('/wiki/tést/')
+        wiki_page2 = self.app.get(h.urlquote('/wiki/tést/'))
         assert not wiki_page2.html.find('div', {'class': 'editbox'})
 
     def test_change_home_page(self):
-        self.app.post('/wiki/tést/update', params={
-            'title': 'our_néw_home',
+        self.app.post(h.urlquote('/wiki/tést/update'), params={
+            'title': 'our_néw_home'.encode('utf-8'),
             'text': 'sometext',
             'labels': '',
             })
@@ -669,7 +670,7 @@ class TestRootController(TestController):
         homepage_admin.form['new_home'].value = 'our_néw_home'
         homepage_admin.form.submit()
         root_path = self.app.get('/wiki/', status=302)
-        assert root_path.location.endswith('/wiki/our_néw_home/'), root_path.location
+        assert root_path.location.endswith('/wiki/our_n%C3%A9w_home/'), root_path.location
 
     def test_edit_mount_label(self):
         r = self.app.get('/admin/wiki/edit_label', validate_chunk=True)
@@ -944,7 +945,7 @@ class TestRootController(TestController):
 
     def test_sidebar_admin_menu_invisible_to_not_admin(self):
         def assert_invisible_for(username):
-            env = {'username': username}
+            env = {'username': str(username)}
             r = self.app.get('/p/test/wiki/Home/', extra_environ=env)
             menu = r.html.find('div', {'id': 'sidebar-admin-menu'})
             assert_equal(menu, None)
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index d089974..5b550af 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -388,7 +388,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
     @with_trailing_slash
     @expose()
     def index(self, **kw):
-        redirect(h.really_unicode(c.app.root_page_name).encode('utf-8') + '/')
+        redirect(h.urlquote(h.really_unicode(c.app.root_page_name)+ '/'))
 
     @expose()
     def _lookup(self, pname, *remainder):
@@ -397,7 +397,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
 
     @expose()
     def new_page(self, title):
-        redirect(h.really_unicode(title).encode('utf-8') + '/')
+        redirect(h.urlquote(h.really_unicode(title) + '/'))
 
     @with_trailing_slash
     @expose('jinja:forgewiki:templates/wiki/search.html')
@@ -674,7 +674,7 @@ class PageController(BaseController, FeedController):
         return dict(p1=p1, p2=p2, edits=result)
 
     @without_trailing_slash
-    @expose(content_type='text/plain')
+    @expose(content_type=str('text/plain'))
     def raw(self):
         if not self.page:
             raise exc.HTTPNotFound
@@ -756,8 +756,7 @@ class PageController(BaseController, FeedController):
             notification_tasks.send_usermentions_notification.post(self.page.index_id(), text, old_text)
         g.director.create_activity(c.user, activity_verb, self.page,
                                    related_nodes=[c.project], tags=['wiki'])
-        redirect('../' + h.really_unicode(self.page.title)
-                 .encode('utf-8') + ('/' if not name_conflict else '/edit'))
+        redirect('../' + h.urlquote(h.really_unicode(self.page.title)) + ('/' if not name_conflict else '/edit'))
 
     @without_trailing_slash
     @expose('json:')
@@ -927,9 +926,8 @@ class WikiAdminController(DefaultAdminController):
         flash('Home updated')
         mount_base = c.project.url() + \
             self.app.config.options.mount_point + '/'
-        url = h.really_unicode(mount_base).encode('utf-8') + \
-            h.really_unicode(new_home).encode('utf-8') + '/'
-        redirect(url)
+        url = h.really_unicode(mount_base) + h.really_unicode(new_home) + '/'
+        redirect(h.urlquote(url))
 
     @without_trailing_slash
     @expose()
diff --git a/scripts/perf/call_count.py b/scripts/perf/call_count.py
index 669e95e..c424d82 100755
--- a/scripts/perf/call_count.py
+++ b/scripts/perf/call_count.py
@@ -116,7 +116,7 @@ def generate_wiki_thread(test):
 def count_page(test, url, verbose=False, debug_html=False):
 
     with LogCapture('stats') as stats, LogCapture('timermiddleware') as calls:
-        resp = test.app.get(url, extra_environ=dict(username='*anonymous'))
+        resp = test.app.get(url, extra_environ=dict(username=str('*anonymous')))
         print url, resp.status
         if debug_html:
             debug_filename = 'call-{}.html'.format(''.join([random.choice(string.ascii_letters + string.digits)