From 7fa1ee007d96d161cea414be47fab015bf2a678e Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Jan 27 2017 17:11:01 +0000
Subject: [PATCH 1/3] More Python3 compatibility


---

diff --git a/hubs/app.py b/hubs/app.py
index f352afd..f75b404 100755
--- a/hubs/app.py
+++ b/hubs/app.py
@@ -5,6 +5,7 @@ import functools
 import json
 import logging
 import os
+import six
 from six.moves.urllib import parse as urlparse
 
 import flask
@@ -20,7 +21,7 @@ import hubs.stream
 
 import datanommer.models
 
-from hubs.utils import username2avatar
+from hubs.utils import username2avatar, to_str
 
 app = flask.Flask(__name__)
 
@@ -546,7 +547,7 @@ def notifications(user):
         return flask.jsonify(notifications)
 
     if flask.request.method == 'POST':
-        data = json.loads(flask.request.data)
+        data = json.loads(to_str(flask.request.data))
         user = hubs.models.User.by_username(session, user)
         if not user:
             return flask.abort(400)
@@ -756,7 +757,7 @@ def plus_plus_status(user):
 def plus_plus_update_bool_helper(val):
     if isinstance(val, bool):
         return val
-    elif isinstance(val, (str, unicode)):
+    elif isinstance(val, six.string_types):
         fmt_str = str(val).replace("'", "").replace('"', '').lower()
         return fmt_str in ("yes", "true", "t", "1")
     else:
diff --git a/hubs/tests/test_api/test_notifications.py b/hubs/tests/test_api/test_notifications.py
index 8440baf..66f8d99 100644
--- a/hubs/tests/test_api/test_notifications.py
+++ b/hubs/tests/test_api/test_notifications.py
@@ -4,6 +4,7 @@ from six.moves.urllib.parse import urlparse
 import hubs.tests
 import hubs.models
 from hubs.app import app
+from hubs.utils import to_str
 
 
 class TestGetNotifications(hubs.tests.APPTest):
@@ -15,7 +16,7 @@ class TestGetNotifications(hubs.tests.APPTest):
         with hubs.tests.auth_set(app, self.user):
             resp = self.app.get('/{}/notifications/'.format(name))
         self.assertEqual(resp.status_code, 200)
-        data = json.loads(resp.data)
+        data = json.loads(to_str(resp.data))
         self.assertEqual(data, [])
 
     def test_get_notifications_valid_name(self):
@@ -24,7 +25,7 @@ class TestGetNotifications(hubs.tests.APPTest):
                 self.user.username))
 
         self.assertEqual(resp.status_code, 200)
-        data = json.loads(resp.data)
+        data = json.loads(to_str(resp.data))
         self.assertEqual(len(data), 1)
 
         for saved in data:
@@ -67,7 +68,7 @@ class TestPostNotifications(hubs.tests.APPTest):
                 data=json.dumps(self.valid_payload))
 
         self.assertEqual(resp.status_code, 200)
-        data = json.loads(resp.data)
+        data = json.loads(to_str(resp.data))
         self.assertTrue(isinstance(data, dict))
 
         notification = data['notification']
diff --git a/hubs/tests/test_fedora_hubs_flask_api.py b/hubs/tests/test_fedora_hubs_flask_api.py
index ea4b6b9..d94ef4a 100644
--- a/hubs/tests/test_fedora_hubs_flask_api.py
+++ b/hubs/tests/test_fedora_hubs_flask_api.py
@@ -11,6 +11,7 @@ from werkzeug.datastructures import ImmutableMultiDict
 import hubs
 from hubs import tests
 from hubs.app import app
+from hubs.utils import to_str
 
 import hubs.models
 
@@ -287,10 +288,10 @@ class HubsAPITest(hubs.tests.APPTest):
         with tests.auth_set(app, user):
             url = '/visit/decause'
             result = self.app.get(url)
-            self.assertEqual(json.loads(result.data), {"count": 0})
+            self.assertEqual(json.loads(to_str(result.data)), {"count": 0})
 
             result = self.app.post(url)
-            self.assertEqual(json.loads(result.data), {"count": 1})
+            self.assertEqual(json.loads(to_str(result.data)), {"count": 1})
 
             # accessing my hub shouldn't increment the count
             url = 'visit/ralph'
@@ -415,7 +416,7 @@ class HubsAPITest(hubs.tests.APPTest):
             "total": 0,
             "username": "decause"
         }
-        self.assertEqual(json.loads(result.data), expected)
+        self.assertEqual(load_json(result.data), expected)
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_valid(self, mock_post):
@@ -431,7 +432,7 @@ class HubsAPITest(hubs.tests.APPTest):
                 "total": 1,
                 "username": "decause"
             }
-            self.assertEqual(json.loads(result.data), expected)
+            self.assertEqual(load_json(result.data), expected)
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_myself_error(self, mock_post):
diff --git a/hubs/tests/test_widgets/test_about.py b/hubs/tests/test_widgets/test_about.py
index f67e476..2e358d0 100644
--- a/hubs/tests/test_widgets/test_about.py
+++ b/hubs/tests/test_widgets/test_about.py
@@ -3,6 +3,7 @@ import json
 from nose.tools import assert_dict_equal
 
 import hubs.tests.test_widgets
+from hubs.utils import to_str
 
 
 class TestBadges(hubs.tests.test_widgets.WidgetTest):
@@ -12,7 +13,7 @@ class TestBadges(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance('ralph', self.plugin)
         response = self.app.get('/ralph/%i/json/' % widget.idx)
         assert response.status_code == 200, response.status_code
-        data = json.loads(response.data)
+        data = json.loads(to_str(response.data))
         del data['data']['widget_url']
         self.assertDictEqual(data['data'], {
             u'edit_url': u'/ralph/56/edit',
diff --git a/hubs/tests/test_widgets/test_badges.py b/hubs/tests/test_widgets/test_badges.py
index b3b5197..b7831c0 100644
--- a/hubs/tests/test_widgets/test_badges.py
+++ b/hubs/tests/test_widgets/test_badges.py
@@ -2,6 +2,7 @@ import json
 
 from mock import patch, MagicMock
 import hubs.tests.test_widgets
+from hubs.utils import to_str
 
 
 class TestBadges(hubs.tests.test_widgets.WidgetTest):
@@ -11,7 +12,7 @@ class TestBadges(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance('ralph', self.plugin)
         response = self.app.get('/ralph/%i/json/' % widget.idx)
         assert response.status_code == 200, response.status_code
-        data = json.loads(response.data)
+        data = json.loads(to_str(response.data))
         self.assertEquals(data['plugin'], 'badges')
         self.assertIn('assertions', data['data'].keys())
 
@@ -45,4 +46,4 @@ class TestBadges(hubs.tests.test_widgets.WidgetTest):
         msg = {'topic': 'hubs.widget.update', 'msg': {'widget': {'id': widget.idx + 1}}}
         module = hubs.widgets.registry[widget.plugin]
         result = module.should_invalidate(msg, self.session, widget)
-        self.assertTrue(result)
\ No newline at end of file
+        self.assertTrue(result)
diff --git a/hubs/tests/test_widgets/test_fedmsgstats.py b/hubs/tests/test_widgets/test_fedmsgstats.py
index c88b7a4..5f16417 100644
--- a/hubs/tests/test_widgets/test_fedmsgstats.py
+++ b/hubs/tests/test_widgets/test_fedmsgstats.py
@@ -1,6 +1,7 @@
 import json
 
 import hubs.tests.test_widgets
+from hubs.utils import to_str
 
 
 class TestFedmsgStats(hubs.tests.test_widgets.WidgetTest):
@@ -10,7 +11,7 @@ class TestFedmsgStats(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance('ralph', self.plugin)
         response = self.app.get('/ralph/%i/json/' % widget.idx)
         assert response.status_code == 200, response.status_code
-        data = json.loads(response.data)
+        data = json.loads(to_str(response.data))
         del data['data']['widget_url']
         self.assertDictEqual(data['data'], {
             u'edit_url': u'/ralph/37/edit',
diff --git a/hubs/tests/test_widgets/test_library.py b/hubs/tests/test_widgets/test_library.py
index bd01019..58376c4 100644
--- a/hubs/tests/test_widgets/test_library.py
+++ b/hubs/tests/test_widgets/test_library.py
@@ -2,6 +2,7 @@ import json
 
 import hubs.tests.test_widgets
 import hubs.models
+from hubs.utils import to_str
 
 class TestLibrary(hubs.tests.test_widgets.WidgetTest):
     plugin = 'library'  # The name in hubs.widgets.registry
@@ -10,7 +11,7 @@ class TestLibrary(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance('ralph', self.plugin)
         response = self.app.get('/ralph/%i/json/' % widget.idx)
         self.assertEqual(response.status_code, 200)
-        data = json.loads(response.data)
+        data = json.loads(to_str(response.data))
         expected_dict = {
             "config": {
                 "urls": "ralph/"
diff --git a/hubs/tests/test_widgets/test_meetings.py b/hubs/tests/test_widgets/test_meetings.py
index 7184796..5df4bd5 100644
--- a/hubs/tests/test_widgets/test_meetings.py
+++ b/hubs/tests/test_widgets/test_meetings.py
@@ -1,6 +1,7 @@
 import json
 
 import hubs.tests.test_widgets
+from hubs.utils import to_str
 
 
 class TestMeetings(hubs.tests.test_widgets.WidgetTest):
@@ -11,7 +12,7 @@ class TestMeetings(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance(team, self.plugin)
         response = self.app.get('/%s/%i/json/' % (team, widget.idx))
         self.assertEqual(200, response.status_code)
-        data = json.loads(response.data)
+        data = json.loads(to_str(response.data))
         calendar_name = data['data']['calendar']
         self.assertIn(team, calendar_name)
 
@@ -21,4 +22,4 @@ class TestMeetings(hubs.tests.test_widgets.WidgetTest):
         response = self.app.get('/%s/%i/' % (team, widget.idx))
         self.assertEqual(200, response.status_code)
         self.assertIn('i18n', response.data)
-        self.assertIn('<strong>Request A New Meeting</strong>', response.data)
\ No newline at end of file
+        self.assertIn('<strong>Request A New Meeting</strong>', response.data)
diff --git a/hubs/utils.py b/hubs/utils.py
index ed00d80..f5166bd 100755
--- a/hubs/utils.py
+++ b/hubs/utils.py
@@ -1,9 +1,11 @@
+import json
 import logging
 from hashlib import sha256
 
 import arrow
 import markdown
 import requests
+import six
 
 from six.moves.urllib_parse import urlencode
 
@@ -80,3 +82,14 @@ def _github_results(url, auth):
                     part.split('; ')[0][1:-1],
                 ) for part in field.split(', ')
             ])
+
+
+def to_str(data):
+    """
+    A Python3-compatible way to return a string from a string or a UTF8-encoded
+    bytestring. Werkzeug responses are bytestrings on Python 3.
+    """
+    if isinstance(data, six.binary_type):
+        return data.decode('utf-8')
+    else:
+        return data

From cfa2cef4ee61ceb4dc080c51db313de26c6fef05 Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Jan 28 2017 09:04:21 +0000
Subject: [PATCH 2/3] Fix string/unicode compat issues with Python 3


---

diff --git a/check-cache-coverage.py b/check-cache-coverage.py
index 0f6c6f7..a753f56 100755
--- a/check-cache-coverage.py
+++ b/check-cache-coverage.py
@@ -13,6 +13,8 @@ forcibly re-warms the cache.
 Authors:    Ralph Bean <rbean@redhat.com>
 """
 
+from __future__ import unicode_literals
+
 import sys
 import dogpile.cache
 
diff --git a/check-queue-length.py b/check-queue-length.py
index 4fc7098..5b14d0f 100755
--- a/check-queue-length.py
+++ b/check-queue-length.py
@@ -13,6 +13,8 @@ We could also graph this in collectd, for fun and profit.
 Authors:    Ralph Bean <rbean@redhat.com>
 """
 
+from __future__ import unicode_literals
+
 import fedmsg.config
 config = fedmsg.config.load_config()
 
diff --git a/delete-user.py b/delete-user.py
index acc2563..42a17c1 100755
--- a/delete-user.py
+++ b/delete-user.py
@@ -4,6 +4,8 @@
 Useful for testing what happens when you recreate them.
 """
 
+from __future__ import unicode_literals
+
 import sys
 
 import fedmsg.config
diff --git a/hubs/app.py b/hubs/app.py
index f75b404..ca3bd26 100755
--- a/hubs/app.py
+++ b/hubs/app.py
@@ -1,4 +1,4 @@
-from __future__ import print_function
+from __future__ import print_function, unicode_literals
 
 import datetime
 import functools
@@ -455,7 +455,7 @@ def widget_source(name):
     fname = ''
 
     try:
-        fname = base + registry[name].__file__.split(base, 1)[1]
+        fname = base + registry[name].__file__.split(base)[-1]
     except KeyError:
         flask.abort(404)
 
diff --git a/hubs/backend/consumer.py b/hubs/backend/consumer.py
index 9d73ef1..7ea5083 100755
--- a/hubs/backend/consumer.py
+++ b/hubs/backend/consumer.py
@@ -1,5 +1,7 @@
 # The fedora hubs backend daemon
 
+from __future__ import unicode_literals
+
 import fedmsg.consumers
 import fedmsg.meta
 
diff --git a/hubs/backend/triage.py b/hubs/backend/triage.py
index ad86221..c1bf1da 100755
--- a/hubs/backend/triage.py
+++ b/hubs/backend/triage.py
@@ -19,6 +19,8 @@ though.  If they see that invalidation is required, they put a new task on an
 handle.
 """
 
+from __future__ import unicode_literals
+
 import hubs.app
 import hubs.models
 
diff --git a/hubs/backend/worker.py b/hubs/backend/worker.py
index 11c3342..abc1b32 100755
--- a/hubs/backend/worker.py
+++ b/hubs/backend/worker.py
@@ -24,6 +24,8 @@ another redis queue so that an EventSource server can update any connected web
 clients with the new content produced here.
 """
 
+from __future__ import unicode_literals
+
 import hubs.app
 import hubs.models
 
diff --git a/hubs/default_config.py b/hubs/default_config.py
index bb9ef80..769f8ad 100755
--- a/hubs/default_config.py
+++ b/hubs/default_config.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import os
 
 SECRET_KEY = 'changemeforreal'
diff --git a/hubs/defaults.py b/hubs/defaults.py
index 2a27612..d91d452 100755
--- a/hubs/defaults.py
+++ b/hubs/defaults.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 
 import hubs.models
diff --git a/hubs/hinting.py b/hubs/hinting.py
index 14ed445..b1a8c2f 100755
--- a/hubs/hinting.py
+++ b/hubs/hinting.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import decorator
 
 import fedmsg.config
diff --git a/hubs/models.py b/hubs/models.py
index add289e..cdc02de 100755
--- a/hubs/models.py
+++ b/hubs/models.py
@@ -20,6 +20,7 @@
 # of Red Hat, Inc.
 #
 
+from __future__ import unicode_literals
 
 import datetime
 import json
diff --git a/hubs/stream.py b/hubs/stream.py
index fd45923..ced01b9 100644
--- a/hubs/stream.py
+++ b/hubs/stream.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 import os
 
diff --git a/hubs/tests/__init__.py b/hubs/tests/__init__.py
index e7f4eba..3730d4e 100644
--- a/hubs/tests/__init__.py
+++ b/hubs/tests/__init__.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from contextlib import contextmanager
 import json
 from datetime import datetime, timedelta
diff --git a/hubs/tests/test_api/test_hub.py b/hubs/tests/test_api/test_hub.py
index 815411d..821746c 100644
--- a/hubs/tests/test_api/test_hub.py
+++ b/hubs/tests/test_api/test_hub.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import flask
 import unittest
 from six.moves.urllib.parse import urlparse
diff --git a/hubs/tests/test_api/test_notifications.py b/hubs/tests/test_api/test_notifications.py
index 66f8d99..360fa4c 100644
--- a/hubs/tests/test_api/test_notifications.py
+++ b/hubs/tests/test_api/test_notifications.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 from six.moves.urllib.parse import urlparse
 
diff --git a/hubs/tests/test_fedora_hubs_flask_api.py b/hubs/tests/test_fedora_hubs_flask_api.py
index d94ef4a..edfc6a6 100644
--- a/hubs/tests/test_fedora_hubs_flask_api.py
+++ b/hubs/tests/test_fedora_hubs_flask_api.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import unittest
 from six.moves.urllib.parse import urlparse
 import ast # import for string to dict conversion without unicode
@@ -34,16 +36,16 @@ class HubsAPITest(hubs.tests.APPTest):
             # assert the status code of the response
             self.assertEqual(result.status_code, 200)
             self.assertFalse('Not logged in.  Click to <a href="'
-                             '/login">login</a>' in result.data)
+                             '/login">login</a>' in to_str(result.data))
 
     def test_hub_logged_out(self):
         with tests.auth_set(app, None):  # check_auth doesn't load in unittest
             result = self.app.get('/ralph', follow_redirects=True)
             self.assertEqual(result.status_code, 200)
             match_expected = r'<h5[^>]*>Ralph</h5>'
-            self.assertRegexpMatches(result.data, match_expected)
+            self.assertRegexpMatches(to_str(result.data), match_expected)
             str_expected = 'Not logged in.'
-            self.assertTrue(str_expected in result.data)
+            self.assertTrue(str_expected in to_str(result.data))
 
     def test_groups_logged_out(self):
         result = self.app.get('/groups', follow_redirects=False)
@@ -58,7 +60,8 @@ class HubsAPITest(hubs.tests.APPTest):
             result = self.app.get('/groups', follow_redirects=True)
             # assert the status code of the response
             self.assertEqual(result.status_code, 200)
-            self.assertTrue("ZOMG -  is the Hub Of The Month!" in result.data)
+            self.assertTrue("ZOMG -  is the Hub Of The Month!"
+                            in to_str(result.data))
 
     def test_hub_logged_in(self):
         user = tests.FakeAuthorization('ralph')
@@ -66,7 +69,7 @@ class HubsAPITest(hubs.tests.APPTest):
             result = self.app.get('/ralph', follow_redirects=True)
             self.assertEqual(result.status_code, 200)
             self.assertFalse('Not logged in.  Click to <a href="'
-                             '/login">login</a>' in result.data)
+                             '/login">login</a>' in to_str(result.data))
 
     def test_hub_json(self):
         result = self.app.get('/ralph/json', follow_redirects=True)
@@ -84,7 +87,7 @@ class HubsAPITest(hubs.tests.APPTest):
             "summary": "Ralph",
             "widgets": [34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 56]
         }
-        self.assertDictEqual(data, ast.literal_eval(result.data))
+        self.assertDictEqual(data, json.loads(to_str(result.data)))
 
     @unittest.skip("Authorization layer not present yet")
     def test_hub_edit_get_logged_out(self):
@@ -134,9 +137,9 @@ class HubsAPITest(hubs.tests.APPTest):
                                    follow_redirects=True)
             self.assertEqual(result.status_code, 200)
             self.assertFalse('Not logged in.  Click to <a href="'
-                             '/login">login</a>' in result.data)
+                             '/login">login</a>' in to_str(result.data))
             self.assertTrue('<a href="#" class="dropdown-item">Full Name: '
-                            'fullname: ralph</a>' in result.data)
+                            'fullname: ralph</a>' in to_str(result.data))
 
     def test_hub_edit_post_logged_in_owner_valid_data(self):
         user = tests.FakeAuthorization('ralph')
@@ -157,7 +160,7 @@ class HubsAPITest(hubs.tests.APPTest):
             result = self.app.post('/ralph/edit', data=data,
                                    follow_redirects=True)
             self.assertEqual(result.status_code, 200)
-            self.assertEqual(result.data, 'ok')
+            self.assertEqual(to_str(result.data), 'ok')
 
     def test_hub_edit_post_logged_in_owner_invalid_data_1(self):
         user = tests.FakeAuthorization('ralph')
@@ -212,30 +215,30 @@ class HubsAPITest(hubs.tests.APPTest):
         result = self.app.get('/ralph/add', follow_redirects=False)
         self.assertEqual(result.status_code, 400)
         expected_str = 'Invalid position provided'
-        self.assertTrue(expected_str in result.data)
+        self.assertTrue(expected_str in to_str(result.data))
 
     def test_hub_add_widget_get_with_args(self):
         result = self.app.get('/ralph/add?position=right',
                               follow_redirects=True)
         self.assertEqual(result.status_code, 200)
         expected_str = 'Adding widget to hub: ralph'
-        self.assertTrue(expected_str in result.data)
+        self.assertTrue(expected_str in to_str(result.data))
         expected_str = "url: 'add/' + $('#widget').val() + '?position=right',"
-        self.assertTrue(expected_str in result.data)
+        self.assertTrue(expected_str in to_str(result.data))
 
     def test_hub_add_widget_post_no_widget_name(self):
         data = {}
         result = self.app.post('/ralph/add', data=data, follow_redirects=False)
         self.assertEqual(result.status_code, 400)
         expected_str = 'Invalid request sent'
-        self.assertTrue(expected_str in result.data)
+        self.assertTrue(expected_str in to_str(result.data))
 
     def test_hub_add_widget_post_invalid_widget_name(self):
         data = {'widget_name': 'invalid_widget_name'}
         result = self.app.post('/ralph/add', data=data, follow_redirects=False)
         self.assertEqual(result.status_code, 404)
         expected_str = 'Unknown widget called'
-        self.assertTrue(expected_str in result.data)
+        self.assertTrue(expected_str in to_str(result.data))
 
     def test_hub_add_widget_post_valid_widget_name_no_args(self):
         user = tests.FakeAuthorization('ralph')
@@ -245,9 +248,9 @@ class HubsAPITest(hubs.tests.APPTest):
                                    follow_redirects=False)
             self.assertEqual(result.status_code, 200)
             expected_str = '<h6 class="dropdown-header">Account Information</h6>'
-            self.assertTrue(expected_str in result.data)
+            self.assertTrue(expected_str in to_str(result.data))
             expected_str = '<a href="#" class="dropdown-item">Full Name: fullname: ralph</a>'
-            self.assertTrue(expected_str in result.data)
+            self.assertTrue(expected_str in to_str(result.data))
 
     def test_hub_add_widget_post_valid_widget_name_with_args(self):
         user = tests.FakeAuthorization('ralph')
@@ -257,9 +260,9 @@ class HubsAPITest(hubs.tests.APPTest):
                                    follow_redirects=False)
             self.assertEqual(result.status_code, 200)
             expected_str = '<h6 class="dropdown-header">Account Information</h6>'
-            self.assertTrue(expected_str in result.data)
+            self.assertTrue(expected_str in to_str(result.data))
             expected_str = '<a href="#" class="dropdown-item">Full Name: fullname: ralph</a>'
-            self.assertTrue(expected_str in result.data)
+            self.assertTrue(expected_str in to_str(result.data))
 
     def test_hub_edit_widget_get_logged_in(self):
         user = tests.FakeAuthorization('ralph')
@@ -267,7 +270,7 @@ class HubsAPITest(hubs.tests.APPTest):
             result = self.app.get('/ralph/37/edit', follow_redirects=True)
             self.assertEqual(result.status_code, 200)
             expected_str = '/ralph/37/edit'
-            self.assertTrue(expected_str in result.data)
+            self.assertTrue(expected_str in to_str(result.data))
 
     @unittest.skip("Authorization layer not present yet")
     def test_hub_edit_widget_get_logged_out(self):
@@ -314,21 +317,22 @@ class HubsAPITest(hubs.tests.APPTest):
         with tests.auth_set(app, user):
             url = '/visit/decause'
             result = self.app.get(url)
-            self.assertEqual(result.data, '0')
+            self.assertEqual(to_str(result.data), '0')
 
             url = '/decause'
             result = self.app.get(url, follow_redirects=True)
 
             url = '/visit/decause'
             result = self.app.get(url)
-            self.assertEqual(result.data, '1')
+            self.assertEqual(to_str(result.data), '1')
 
     def test_hub_add_widget_valid_side(self):
         user = tests.FakeAuthorization('ralph')
         with tests.auth_set(app, user):
             url = '/ralph/add/about?position=right'
             result = self.app.get(url)
-            self.assertTrue('Adding widget "about"to hub: ralph' in result.data)
+            self.assertTrue('Adding widget "about"to hub: ralph'
+                            in to_str(result.data))
 
     def test_hub_add_widget_invalid_side(self):
         user = tests.FakeAuthorization('ralph')
@@ -343,7 +347,7 @@ class HubsAPITest(hubs.tests.APPTest):
             url = '/ralph/37/delete'  # 37 is widget fedmsgstats for ralph
             result = self.app.post(url)
             self.assertEqual(result.status_code, 302)
-            self.assertTrue('/ralph/edit' in result.data)
+            self.assertTrue('/ralph/edit' in to_str(result.data))
 
     def test_source_name(self):
         with tests.auth_set(app, None):
@@ -352,7 +356,7 @@ class HubsAPITest(hubs.tests.APPTest):
             self.assertEqual(result.status_code, 302)
             expected_str = 'https://pagure.io/fedora-hubs/' \
                            'blob/develop/f/hubs/widgets/about.py'
-            self.assertTrue(expected_str in result.data)
+            self.assertIn(expected_str, to_str(result.data))
 
     def test_source_name_not_existent(self):
         with tests.auth_set(app, None):
@@ -416,7 +420,7 @@ class HubsAPITest(hubs.tests.APPTest):
             "total": 0,
             "username": "decause"
         }
-        self.assertEqual(load_json(result.data), expected)
+        self.assertEqual(json.loads(to_str(result.data)), expected)
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_valid(self, mock_post):
@@ -432,7 +436,7 @@ class HubsAPITest(hubs.tests.APPTest):
                 "total": 1,
                 "username": "decause"
             }
-            self.assertEqual(load_json(result.data), expected)
+            self.assertEqual(json.loads(to_str(result.data)), expected)
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_myself_error(self, mock_post):
@@ -441,7 +445,7 @@ class HubsAPITest(hubs.tests.APPTest):
             url = '/plus_plus/ralph/update'
             result = self.app.post(url, data={'increment': True})
             self.assertEqual(result.status_code, 403)
-            self.assertEqual(result.data, 'You may not modify your own karma.')
+            self.assertEqual(to_str(result.data), 'You may not modify your own karma.')
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_user_does_not_exist(self, mock_post):
@@ -450,7 +454,7 @@ class HubsAPITest(hubs.tests.APPTest):
             url = '/plus_plus/doesnotexist/update'
             result = self.app.post(url, data={'increment': True})
             self.assertEqual(result.status_code, 403)
-            self.assertEqual(result.data, 'User does not exist')
+            self.assertEqual(to_str(result.data), 'User does not exist')
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_no_data_error(self, mock_post):
@@ -461,13 +465,13 @@ class HubsAPITest(hubs.tests.APPTest):
             self.assertEqual(result.status_code, 403)
             exp_str = "You must set 'decrement' or 'increment' " \
                       "with a boolean value in the body"
-            self.assertEqual(result.data, exp_str)
+            self.assertEqual(to_str(result.data), exp_str)
 
     def test_plus_plus_receiver_does_not_exist(self):
         url = '/plus_plus/doesnotexist/status'
         result = self.app.get(url)
         self.assertEqual(result.status_code, 403)
-        self.assertEqual(result.data, 'User does not exist')
+        self.assertEqual(to_str(result.data), 'User does not exist')
 
 
 
diff --git a/hubs/tests/test_models.py b/hubs/tests/test_models.py
index 93f4a31..3065cfb 100644
--- a/hubs/tests/test_models.py
+++ b/hubs/tests/test_models.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import hubs
 import hubs.models
 import hubs.tests
diff --git a/hubs/tests/test_widgets/__init__.py b/hubs/tests/test_widgets/__init__.py
index 9b9ac69..47a2f4f 100644
--- a/hubs/tests/test_widgets/__init__.py
+++ b/hubs/tests/test_widgets/__init__.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 from datetime import datetime, timedelta
 from os.path import dirname
diff --git a/hubs/tests/test_widgets/test_about.py b/hubs/tests/test_widgets/test_about.py
index 2e358d0..f894e4b 100644
--- a/hubs/tests/test_widgets/test_about.py
+++ b/hubs/tests/test_widgets/test_about.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 
 from nose.tools import assert_dict_equal
diff --git a/hubs/tests/test_widgets/test_badges.py b/hubs/tests/test_widgets/test_badges.py
index b7831c0..2dd6458 100644
--- a/hubs/tests/test_widgets/test_badges.py
+++ b/hubs/tests/test_widgets/test_badges.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 
 from mock import patch, MagicMock
diff --git a/hubs/tests/test_widgets/test_fedmsgstats.py b/hubs/tests/test_widgets/test_fedmsgstats.py
index 5f16417..bec2363 100644
--- a/hubs/tests/test_widgets/test_fedmsgstats.py
+++ b/hubs/tests/test_widgets/test_fedmsgstats.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 
 import hubs.tests.test_widgets
diff --git a/hubs/tests/test_widgets/test_library.py b/hubs/tests/test_widgets/test_library.py
index 58376c4..68f2598 100644
--- a/hubs/tests/test_widgets/test_library.py
+++ b/hubs/tests/test_widgets/test_library.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 
 import hubs.tests.test_widgets
diff --git a/hubs/tests/test_widgets/test_meetings.py b/hubs/tests/test_widgets/test_meetings.py
index 5df4bd5..d3b3f09 100644
--- a/hubs/tests/test_widgets/test_meetings.py
+++ b/hubs/tests/test_widgets/test_meetings.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 
 import hubs.tests.test_widgets
@@ -21,5 +23,6 @@ class TestMeetings(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance(team, self.plugin)
         response = self.app.get('/%s/%i/' % (team, widget.idx))
         self.assertEqual(200, response.status_code)
-        self.assertIn('i18n', response.data)
-        self.assertIn('<strong>Request A New Meeting</strong>', response.data)
+        self.assertIn('i18n', to_str(response.data))
+        self.assertIn('<strong>Request A New Meeting</strong>',
+                      to_str(response.data))
diff --git a/hubs/utils.py b/hubs/utils.py
index f5166bd..c858271 100755
--- a/hubs/utils.py
+++ b/hubs/utils.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import json
 import logging
 from hashlib import sha256
@@ -20,10 +22,7 @@ def markup(text):
 
 
 def username2avatar(username, s=312):
-    query = urlencode({
-        'd': 'retro',
-        's': s,
-    })
+    query = urlencode([('s', s), ('d', 'retro')])
     openid = 'http://%s.id.fedoraproject.org/' % username
     hash = sha256(openid.encode('utf-8')).hexdigest()
     avatar = "https://seccdn.libravatar.org/avatar/%s?%s" % (hash, query)
diff --git a/hubs/validators.py b/hubs/validators.py
index 2ae918f..302b0e7 100755
--- a/hubs/validators.py
+++ b/hubs/validators.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import kitchen.text.converters
 
 import hubs.models
diff --git a/hubs/widgets/__init__.py b/hubs/widgets/__init__.py
index 5f416ed..95253e7 100755
--- a/hubs/widgets/__init__.py
+++ b/hubs/widgets/__init__.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.widgets import dummy
 from hubs.widgets import stats
 from hubs.widgets import rules
diff --git a/hubs/widgets/about.py b/hubs/widgets/about.py
index 4be594c..7f0d994 100755
--- a/hubs/widgets/about.py
+++ b/hubs/widgets/about.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument
diff --git a/hubs/widgets/badges.py b/hubs/widgets/badges.py
index abf4b15..86bc929 100755
--- a/hubs/widgets/badges.py
+++ b/hubs/widgets/badges.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import operator
 
 import requests
diff --git a/hubs/widgets/base.py b/hubs/widgets/base.py
index f3cf198..581532e 100755
--- a/hubs/widgets/base.py
+++ b/hubs/widgets/base.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import collections
 import datetime
 import functools
diff --git a/hubs/widgets/bugzilla.py b/hubs/widgets/bugzilla.py
index a33e318..c2d94ae 100644
--- a/hubs/widgets/bugzilla.py
+++ b/hubs/widgets/bugzilla.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import requests
 import pkgwat.api
 
diff --git a/hubs/widgets/chrome.py b/hubs/widgets/chrome.py
index 717e047..b869094 100755
--- a/hubs/widgets/chrome.py
+++ b/hubs/widgets/chrome.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.widgets.base import wraps
 from hubs.widgets import templating
 
diff --git a/hubs/widgets/clean_input.py b/hubs/widgets/clean_input.py
index 2ec458a..9d25104 100644
--- a/hubs/widgets/clean_input.py
+++ b/hubs/widgets/clean_input.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from six.moves.urllib.parse import urlparse
 
 import bleach
diff --git a/hubs/widgets/contact.py b/hubs/widgets/contact.py
index b76873c..b14e126 100644
--- a/hubs/widgets/contact.py
+++ b/hubs/widgets/contact.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import flask
 
 import fedmsg.config
diff --git a/hubs/widgets/dummy.py b/hubs/widgets/dummy.py
index 2e73bca..bd198d3 100755
--- a/hubs/widgets/dummy.py
+++ b/hubs/widgets/dummy.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument
diff --git a/hubs/widgets/fedmsgstats.py b/hubs/widgets/fedmsgstats.py
index 533c563..0cf5ba2 100755
--- a/hubs/widgets/fedmsgstats.py
+++ b/hubs/widgets/fedmsgstats.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint
 from hubs.widgets.chrome import panel
 from hubs.widgets import templating
diff --git a/hubs/widgets/feed.py b/hubs/widgets/feed.py
index 8ea6536..5d744d9 100755
--- a/hubs/widgets/feed.py
+++ b/hubs/widgets/feed.py
@@ -1,4 +1,4 @@
-from __future__ import print_function
+from __future__ import print_function, unicode_literals
 
 from hubs.hinting import hint
 from hubs.widgets.base import argument
diff --git a/hubs/widgets/fhosted.py b/hubs/widgets/fhosted.py
index 827b228..bc97d8b 100644
--- a/hubs/widgets/fhosted.py
+++ b/hubs/widgets/fhosted.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from six.moves.xmlrpc_client import ServerProxy
 
 import hubs.validators as validators
diff --git a/hubs/widgets/github_pr.py b/hubs/widgets/github_pr.py
index b2d1788..c2a0be9 100755
--- a/hubs/widgets/github_pr.py
+++ b/hubs/widgets/github_pr.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import logging
 
 from hubs.widgets.chrome import panel
diff --git a/hubs/widgets/githubissues.py b/hubs/widgets/githubissues.py
index 986ff11..7130183 100755
--- a/hubs/widgets/githubissues.py
+++ b/hubs/widgets/githubissues.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import requests
 
 from hubs.hinting import hint
diff --git a/hubs/widgets/library.py b/hubs/widgets/library.py
index b18354a..08ad334 100644
--- a/hubs/widgets/library.py
+++ b/hubs/widgets/library.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument
@@ -21,9 +23,9 @@ def data(session, widget, urls):
 
     urls = [
         clean_input.clean(
-            '<a href="{0}">{0}</a>'.format(u.encode('utf-8').strip()))
+            '<a href="{0}">{0}</a>'.format(u.strip()))
         for u in widget.config.get('urls', '').split(',')
-        if u.encode('utf-8').strip()
+        if u.strip()
     ]
     return dict(urls=urls)
 
diff --git a/hubs/widgets/linechart.py b/hubs/widgets/linechart.py
index f12ef8f..3d41057 100755
--- a/hubs/widgets/linechart.py
+++ b/hubs/widgets/linechart.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint
 from hubs.widgets.base import argument
 from hubs.widgets import templating
diff --git a/hubs/widgets/meetings.py b/hubs/widgets/meetings.py
index 8c05744..713177b 100755
--- a/hubs/widgets/meetings.py
+++ b/hubs/widgets/meetings.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint
 from hubs.widgets.base import argument
 from hubs.widgets.chrome import panel
diff --git a/hubs/widgets/memberships.py b/hubs/widgets/memberships.py
index 48d8fea..2763db0 100644
--- a/hubs/widgets/memberships.py
+++ b/hubs/widgets/memberships.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import hubs.models
 from hubs.hinting import hint, prefixed as _
 import hubs.validators as validators
diff --git a/hubs/widgets/pagure_pr.py b/hubs/widgets/pagure_pr.py
index cdb8365..081dcb4 100755
--- a/hubs/widgets/pagure_pr.py
+++ b/hubs/widgets/pagure_pr.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument
diff --git a/hubs/widgets/pagureissues.py b/hubs/widgets/pagureissues.py
index c9ecd53..9a4dca8 100755
--- a/hubs/widgets/pagureissues.py
+++ b/hubs/widgets/pagureissues.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import requests
 
 from hubs.hinting import hint
diff --git a/hubs/widgets/rules.py b/hubs/widgets/rules.py
index 443f07c..d7554b9 100755
--- a/hubs/widgets/rules.py
+++ b/hubs/widgets/rules.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from collections import OrderedDict as ordereddict
 
 from hubs.hinting import hint, prefixed as _
diff --git a/hubs/widgets/stats.py b/hubs/widgets/stats.py
index 55c90db..ebd6cc8 100755
--- a/hubs/widgets/stats.py
+++ b/hubs/widgets/stats.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
 from hubs.widgets import templating
diff --git a/hubs/widgets/sticky.py b/hubs/widgets/sticky.py
index 55cc6a1..ca74f59 100755
--- a/hubs/widgets/sticky.py
+++ b/hubs/widgets/sticky.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.chrome import panel
 from hubs.widgets.base import argument
diff --git a/hubs/widgets/subscriptions.py b/hubs/widgets/subscriptions.py
index 82fce66..8035d3d 100755
--- a/hubs/widgets/subscriptions.py
+++ b/hubs/widgets/subscriptions.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from hubs.hinting import hint, prefixed as _
 from hubs.widgets.base import argument
 from hubs.widgets.chrome import panel
diff --git a/hubs/widgets/templating.py b/hubs/widgets/templating.py
index 98b29cd..41e0c5b 100644
--- a/hubs/widgets/templating.py
+++ b/hubs/widgets/templating.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import os
 
 import jinja2
diff --git a/hubs/widgets/workflow/pendingacls.py b/hubs/widgets/workflow/pendingacls.py
index 581bd70..5bc6594 100644
--- a/hubs/widgets/workflow/pendingacls.py
+++ b/hubs/widgets/workflow/pendingacls.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import requests
 
 from hubs.hinting import hint, prefixed as _
diff --git a/hubs/widgets/workflow/updates2stable.py b/hubs/widgets/workflow/updates2stable.py
index 675d793..c7df1c3 100644
--- a/hubs/widgets/workflow/updates2stable.py
+++ b/hubs/widgets/workflow/updates2stable.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import requests
 
 from hubs.hinting import hint, prefixed as _
diff --git a/populate-from-fas.py b/populate-from-fas.py
index 7639c81..b74e8a6 100755
--- a/populate-from-fas.py
+++ b/populate-from-fas.py
@@ -1,6 +1,8 @@
 #!/usr/bin/env python
 """ Populate the hubs db with lots of data from FAS. """
 
+from __future__ import unicode_literals
+
 import getpass
 import socket
 import string
diff --git a/populate.py b/populate.py
index 5c34700..4286940 100755
--- a/populate.py
+++ b/populate.py
@@ -1,6 +1,8 @@
 #!/usr/bin/env python
 """ Populate the hubs db with some dev data. """
 
+from __future__ import unicode_literals
+
 import json
 
 import hubs.models
diff --git a/runserver.py b/runserver.py
index 5e5a4f2..9552b0a 100755
--- a/runserver.py
+++ b/runserver.py
@@ -1,5 +1,7 @@
 #!/usr/bin/env python2
 
+from __future__ import unicode_literals
+
 import argparse
 import sys
 import os
diff --git a/setup.py b/setup.py
index 15f69c2..aa9b63e 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,7 @@
 """ Setup file for fedora hubs """
 
+from __future__ import unicode_literals
+
 from setuptools import setup, find_packages
 
 
diff --git a/smart_cache_invalidator.py b/smart_cache_invalidator.py
index 77021fc..25c2540 100755
--- a/smart_cache_invalidator.py
+++ b/smart_cache_invalidator.py
@@ -8,7 +8,7 @@ This is really useful for development purposes as it allow seeing changes
 made to a widget without dropping the entire cache database.
 """
 
-from __future__ import print_function
+from __future__ import print_function, unicode_literals
 
 import argparse
 import os

From 56cff3a43d52bf58a4dbb7fa376949e95fc1df5a Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Jan 31 2017 17:14:37 +0000
Subject: [PATCH 3/3] Use request.get_json() and .get_data() where approriate


---

diff --git a/hubs/app.py b/hubs/app.py
index ca3bd26..89b5271 100755
--- a/hubs/app.py
+++ b/hubs/app.py
@@ -21,7 +21,7 @@ import hubs.stream
 
 import datanommer.models
 
-from hubs.utils import username2avatar, to_str
+from hubs.utils import username2avatar
 
 app = flask.Flask(__name__)
 
@@ -547,7 +547,7 @@ def notifications(user):
         return flask.jsonify(notifications)
 
     if flask.request.method == 'POST':
-        data = json.loads(to_str(flask.request.data))
+        data = flask.request.get_json()
         user = hubs.models.User.by_username(session, user)
         if not user:
             return flask.abort(400)
diff --git a/hubs/tests/test_api/test_notifications.py b/hubs/tests/test_api/test_notifications.py
index 360fa4c..5b8ad9e 100644
--- a/hubs/tests/test_api/test_notifications.py
+++ b/hubs/tests/test_api/test_notifications.py
@@ -6,7 +6,6 @@ from six.moves.urllib.parse import urlparse
 import hubs.tests
 import hubs.models
 from hubs.app import app
-from hubs.utils import to_str
 
 
 class TestGetNotifications(hubs.tests.APPTest):
@@ -18,7 +17,7 @@ class TestGetNotifications(hubs.tests.APPTest):
         with hubs.tests.auth_set(app, self.user):
             resp = self.app.get('/{}/notifications/'.format(name))
         self.assertEqual(resp.status_code, 200)
-        data = json.loads(to_str(resp.data))
+        data = json.loads(resp.get_data(as_text=True))
         self.assertEqual(data, [])
 
     def test_get_notifications_valid_name(self):
@@ -27,7 +26,7 @@ class TestGetNotifications(hubs.tests.APPTest):
                 self.user.username))
 
         self.assertEqual(resp.status_code, 200)
-        data = json.loads(to_str(resp.data))
+        data = json.loads(resp.get_data(as_text=True))
         self.assertEqual(len(data), 1)
 
         for saved in data:
@@ -53,24 +52,27 @@ class TestPostNotifications(hubs.tests.APPTest):
         with hubs.tests.auth_set(app, self.user):
             resp = self.app.post(
                 '/{}/notifications/'.format('notarealfasuser'),
-                data=json.dumps(self.valid_payload))
+                data=json.dumps(self.valid_payload),
+                content_type='application/json')
         self.assertEqual(resp.status_code, 400)
 
     def test_post_notification_invalid_payload(self):
         with hubs.tests.auth_set(app, self.user):
             resp = self.app.post(
                 '/{}/notifications/'.format(self.user.username),
-                data=json.dumps(self.invalid_payload))
+                data=json.dumps(self.invalid_payload),
+                content_type='application/json')
         self.assertEqual(resp.status_code, 400)
 
     def test_post_notification_valid_payload(self):
         with hubs.tests.auth_set(app, self.user):
             resp = self.app.post(
                 '/{}/notifications/'.format(self.user.username),
-                data=json.dumps(self.valid_payload))
+                data=json.dumps(self.valid_payload),
+                content_type='application/json')
 
         self.assertEqual(resp.status_code, 200)
-        data = json.loads(to_str(resp.data))
+        data = json.loads(resp.get_data(as_text=True))
         self.assertTrue(isinstance(data, dict))
 
         notification = data['notification']
diff --git a/hubs/tests/test_fedora_hubs_flask_api.py b/hubs/tests/test_fedora_hubs_flask_api.py
index edfc6a6..3c24905 100644
--- a/hubs/tests/test_fedora_hubs_flask_api.py
+++ b/hubs/tests/test_fedora_hubs_flask_api.py
@@ -13,7 +13,6 @@ from werkzeug.datastructures import ImmutableMultiDict
 import hubs
 from hubs import tests
 from hubs.app import app
-from hubs.utils import to_str
 
 import hubs.models
 
@@ -35,17 +34,19 @@ class HubsAPITest(hubs.tests.APPTest):
             # its trying to redirect to login id.fedoraproject.org/openid
             # assert the status code of the response
             self.assertEqual(result.status_code, 200)
-            self.assertFalse('Not logged in.  Click to <a href="'
-                             '/login">login</a>' in to_str(result.data))
+            self.assertNotIn(
+                'Not logged in.  Click to <a href="/login">login</a>',
+                result.get_data(as_text=True))
 
     def test_hub_logged_out(self):
         with tests.auth_set(app, None):  # check_auth doesn't load in unittest
             result = self.app.get('/ralph', follow_redirects=True)
             self.assertEqual(result.status_code, 200)
             match_expected = r'<h5[^>]*>Ralph</h5>'
-            self.assertRegexpMatches(to_str(result.data), match_expected)
+            self.assertRegexpMatches(
+                result.get_data(as_text=True), match_expected)
             str_expected = 'Not logged in.'
-            self.assertTrue(str_expected in to_str(result.data))
+            self.assertIn(str_expected, result.get_data(as_text=True))
 
     def test_groups_logged_out(self):
         result = self.app.get('/groups', follow_redirects=False)
@@ -60,16 +61,17 @@ class HubsAPITest(hubs.tests.APPTest):
             result = self.app.get('/groups', follow_redirects=True)
             # assert the status code of the response
             self.assertEqual(result.status_code, 200)
-            self.assertTrue("ZOMG -  is the Hub Of The Month!"
-                            in to_str(result.data))
+            self.assertIn("ZOMG -  is the Hub Of The Month!",
+                          result.get_data(as_text=True))
 
     def test_hub_logged_in(self):
         user = tests.FakeAuthorization('ralph')
         with tests.auth_set(app, user):
             result = self.app.get('/ralph', follow_redirects=True)
             self.assertEqual(result.status_code, 200)
-            self.assertFalse('Not logged in.  Click to <a href="'
-                             '/login">login</a>' in to_str(result.data))
+            self.assertNotIn(
+                'Not logged in.  Click to <a href="/login">login</a>',
+                result.get_data(as_text=True))
 
     def test_hub_json(self):
         result = self.app.get('/ralph/json', follow_redirects=True)
@@ -87,7 +89,7 @@ class HubsAPITest(hubs.tests.APPTest):
             "summary": "Ralph",
             "widgets": [34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 56]
         }
-        self.assertDictEqual(data, json.loads(to_str(result.data)))
+        self.assertDictEqual(data, json.loads(result.get_data(as_text=True)))
 
     @unittest.skip("Authorization layer not present yet")
     def test_hub_edit_get_logged_out(self):
@@ -136,10 +138,12 @@ class HubsAPITest(hubs.tests.APPTest):
             result = self.app.post('/ralph/edit', data={},
                                    follow_redirects=True)
             self.assertEqual(result.status_code, 200)
-            self.assertFalse('Not logged in.  Click to <a href="'
-                             '/login">login</a>' in to_str(result.data))
-            self.assertTrue('<a href="#" class="dropdown-item">Full Name: '
-                            'fullname: ralph</a>' in to_str(result.data))
+            self.assertNotIn(
+                'Not logged in.  Click to <a href="/login">login</a>',
+                result.get_data(as_text=True))
+            self.assertIn(
+                '<a href="#" class="dropdown-item">Full Name: '
+                'fullname: ralph</a>', result.get_data(as_text=True))
 
     def test_hub_edit_post_logged_in_owner_valid_data(self):
         user = tests.FakeAuthorization('ralph')
@@ -160,7 +164,7 @@ class HubsAPITest(hubs.tests.APPTest):
             result = self.app.post('/ralph/edit', data=data,
                                    follow_redirects=True)
             self.assertEqual(result.status_code, 200)
-            self.assertEqual(to_str(result.data), 'ok')
+            self.assertEqual(result.get_data(as_text=True), 'ok')
 
     def test_hub_edit_post_logged_in_owner_invalid_data_1(self):
         user = tests.FakeAuthorization('ralph')
@@ -215,30 +219,30 @@ class HubsAPITest(hubs.tests.APPTest):
         result = self.app.get('/ralph/add', follow_redirects=False)
         self.assertEqual(result.status_code, 400)
         expected_str = 'Invalid position provided'
-        self.assertTrue(expected_str in to_str(result.data))
+        self.assertIn(expected_str, result.get_data(as_text=True))
 
     def test_hub_add_widget_get_with_args(self):
         result = self.app.get('/ralph/add?position=right',
                               follow_redirects=True)
         self.assertEqual(result.status_code, 200)
         expected_str = 'Adding widget to hub: ralph'
-        self.assertTrue(expected_str in to_str(result.data))
+        self.assertIn(expected_str, result.get_data(as_text=True))
         expected_str = "url: 'add/' + $('#widget').val() + '?position=right',"
-        self.assertTrue(expected_str in to_str(result.data))
+        self.assertIn(expected_str, result.get_data(as_text=True))
 
     def test_hub_add_widget_post_no_widget_name(self):
         data = {}
         result = self.app.post('/ralph/add', data=data, follow_redirects=False)
         self.assertEqual(result.status_code, 400)
         expected_str = 'Invalid request sent'
-        self.assertTrue(expected_str in to_str(result.data))
+        self.assertIn(expected_str, result.get_data(as_text=True))
 
     def test_hub_add_widget_post_invalid_widget_name(self):
         data = {'widget_name': 'invalid_widget_name'}
         result = self.app.post('/ralph/add', data=data, follow_redirects=False)
         self.assertEqual(result.status_code, 404)
         expected_str = 'Unknown widget called'
-        self.assertTrue(expected_str in to_str(result.data))
+        self.assertIn(expected_str, result.get_data(as_text=True))
 
     def test_hub_add_widget_post_valid_widget_name_no_args(self):
         user = tests.FakeAuthorization('ralph')
@@ -248,9 +252,9 @@ class HubsAPITest(hubs.tests.APPTest):
                                    follow_redirects=False)
             self.assertEqual(result.status_code, 200)
             expected_str = '<h6 class="dropdown-header">Account Information</h6>'
-            self.assertTrue(expected_str in to_str(result.data))
+            self.assertIn(expected_str, result.get_data(as_text=True))
             expected_str = '<a href="#" class="dropdown-item">Full Name: fullname: ralph</a>'
-            self.assertTrue(expected_str in to_str(result.data))
+            self.assertIn(expected_str, result.get_data(as_text=True))
 
     def test_hub_add_widget_post_valid_widget_name_with_args(self):
         user = tests.FakeAuthorization('ralph')
@@ -260,9 +264,9 @@ class HubsAPITest(hubs.tests.APPTest):
                                    follow_redirects=False)
             self.assertEqual(result.status_code, 200)
             expected_str = '<h6 class="dropdown-header">Account Information</h6>'
-            self.assertTrue(expected_str in to_str(result.data))
+            self.assertIn(expected_str, result.get_data(as_text=True))
             expected_str = '<a href="#" class="dropdown-item">Full Name: fullname: ralph</a>'
-            self.assertTrue(expected_str in to_str(result.data))
+            self.assertIn(expected_str, result.get_data(as_text=True))
 
     def test_hub_edit_widget_get_logged_in(self):
         user = tests.FakeAuthorization('ralph')
@@ -270,7 +274,7 @@ class HubsAPITest(hubs.tests.APPTest):
             result = self.app.get('/ralph/37/edit', follow_redirects=True)
             self.assertEqual(result.status_code, 200)
             expected_str = '/ralph/37/edit'
-            self.assertTrue(expected_str in to_str(result.data))
+            self.assertIn(expected_str, result.get_data(as_text=True))
 
     @unittest.skip("Authorization layer not present yet")
     def test_hub_edit_widget_get_logged_out(self):
@@ -291,10 +295,14 @@ class HubsAPITest(hubs.tests.APPTest):
         with tests.auth_set(app, user):
             url = '/visit/decause'
             result = self.app.get(url)
-            self.assertEqual(json.loads(to_str(result.data)), {"count": 0})
+            self.assertEqual(
+                json.loads(result.get_data(as_text=True)),
+                {"count": 0})
 
             result = self.app.post(url)
-            self.assertEqual(json.loads(to_str(result.data)), {"count": 1})
+            self.assertEqual(
+                json.loads(result.get_data(as_text=True)),
+                {"count": 1})
 
             # accessing my hub shouldn't increment the count
             url = 'visit/ralph'
@@ -317,22 +325,22 @@ class HubsAPITest(hubs.tests.APPTest):
         with tests.auth_set(app, user):
             url = '/visit/decause'
             result = self.app.get(url)
-            self.assertEqual(to_str(result.data), '0')
+            self.assertEqual(result.get_data(as_text=True), '0')
 
             url = '/decause'
             result = self.app.get(url, follow_redirects=True)
 
             url = '/visit/decause'
             result = self.app.get(url)
-            self.assertEqual(to_str(result.data), '1')
+            self.assertEqual(result.get_data(as_text=True), '1')
 
     def test_hub_add_widget_valid_side(self):
         user = tests.FakeAuthorization('ralph')
         with tests.auth_set(app, user):
             url = '/ralph/add/about?position=right'
             result = self.app.get(url)
-            self.assertTrue('Adding widget "about"to hub: ralph'
-                            in to_str(result.data))
+            self.assertIn('Adding widget "about"to hub: ralph',
+                          result.get_data(as_text=True))
 
     def test_hub_add_widget_invalid_side(self):
         user = tests.FakeAuthorization('ralph')
@@ -347,7 +355,7 @@ class HubsAPITest(hubs.tests.APPTest):
             url = '/ralph/37/delete'  # 37 is widget fedmsgstats for ralph
             result = self.app.post(url)
             self.assertEqual(result.status_code, 302)
-            self.assertTrue('/ralph/edit' in to_str(result.data))
+            self.assertIn('/ralph/edit', result.get_data(as_text=True))
 
     def test_source_name(self):
         with tests.auth_set(app, None):
@@ -356,7 +364,7 @@ class HubsAPITest(hubs.tests.APPTest):
             self.assertEqual(result.status_code, 302)
             expected_str = 'https://pagure.io/fedora-hubs/' \
                            'blob/develop/f/hubs/widgets/about.py'
-            self.assertIn(expected_str, to_str(result.data))
+            self.assertIn(expected_str, result.get_data(as_text=True))
 
     def test_source_name_not_existent(self):
         with tests.auth_set(app, None):
@@ -420,7 +428,9 @@ class HubsAPITest(hubs.tests.APPTest):
             "total": 0,
             "username": "decause"
         }
-        self.assertEqual(json.loads(to_str(result.data)), expected)
+        self.assertEqual(
+            json.loads(result.get_data(as_text=True)),
+            expected)
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_valid(self, mock_post):
@@ -436,7 +446,9 @@ class HubsAPITest(hubs.tests.APPTest):
                 "total": 1,
                 "username": "decause"
             }
-            self.assertEqual(json.loads(to_str(result.data)), expected)
+            self.assertEqual(
+                json.loads(result.get_data(as_text=True)),
+                expected)
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_myself_error(self, mock_post):
@@ -445,7 +457,9 @@ class HubsAPITest(hubs.tests.APPTest):
             url = '/plus_plus/ralph/update'
             result = self.app.post(url, data={'increment': True})
             self.assertEqual(result.status_code, 403)
-            self.assertEqual(to_str(result.data), 'You may not modify your own karma.')
+            self.assertEqual(
+                result.get_data(as_text=True),
+                'You may not modify your own karma.')
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_user_does_not_exist(self, mock_post):
@@ -454,7 +468,9 @@ class HubsAPITest(hubs.tests.APPTest):
             url = '/plus_plus/doesnotexist/update'
             result = self.app.post(url, data={'increment': True})
             self.assertEqual(result.status_code, 403)
-            self.assertEqual(to_str(result.data), 'User does not exist')
+            self.assertEqual(
+                result.get_data(as_text=True),
+                'User does not exist')
 
     @mock.patch('requests.post', side_effect=mocked_requests_post)
     def test_plus_plus_post_increment_no_data_error(self, mock_post):
@@ -465,13 +481,13 @@ class HubsAPITest(hubs.tests.APPTest):
             self.assertEqual(result.status_code, 403)
             exp_str = "You must set 'decrement' or 'increment' " \
                       "with a boolean value in the body"
-            self.assertEqual(to_str(result.data), exp_str)
+            self.assertEqual(result.get_data(as_text=True), exp_str)
 
     def test_plus_plus_receiver_does_not_exist(self):
         url = '/plus_plus/doesnotexist/status'
         result = self.app.get(url)
         self.assertEqual(result.status_code, 403)
-        self.assertEqual(to_str(result.data), 'User does not exist')
+        self.assertEqual(result.get_data(as_text=True), 'User does not exist')
 
 
 
diff --git a/hubs/tests/test_widgets/test_about.py b/hubs/tests/test_widgets/test_about.py
index f894e4b..74e0c56 100644
--- a/hubs/tests/test_widgets/test_about.py
+++ b/hubs/tests/test_widgets/test_about.py
@@ -5,7 +5,6 @@ import json
 from nose.tools import assert_dict_equal
 
 import hubs.tests.test_widgets
-from hubs.utils import to_str
 
 
 class TestBadges(hubs.tests.test_widgets.WidgetTest):
@@ -15,7 +14,7 @@ class TestBadges(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance('ralph', self.plugin)
         response = self.app.get('/ralph/%i/json/' % widget.idx)
         assert response.status_code == 200, response.status_code
-        data = json.loads(to_str(response.data))
+        data = json.loads(response.get_data(as_text=True))
         del data['data']['widget_url']
         self.assertDictEqual(data['data'], {
             u'edit_url': u'/ralph/56/edit',
diff --git a/hubs/tests/test_widgets/test_badges.py b/hubs/tests/test_widgets/test_badges.py
index 2dd6458..8a88807 100644
--- a/hubs/tests/test_widgets/test_badges.py
+++ b/hubs/tests/test_widgets/test_badges.py
@@ -4,7 +4,6 @@ import json
 
 from mock import patch, MagicMock
 import hubs.tests.test_widgets
-from hubs.utils import to_str
 
 
 class TestBadges(hubs.tests.test_widgets.WidgetTest):
@@ -14,7 +13,7 @@ class TestBadges(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance('ralph', self.plugin)
         response = self.app.get('/ralph/%i/json/' % widget.idx)
         assert response.status_code == 200, response.status_code
-        data = json.loads(to_str(response.data))
+        data = json.loads(response.get_data(as_text=True))
         self.assertEquals(data['plugin'], 'badges')
         self.assertIn('assertions', data['data'].keys())
 
diff --git a/hubs/tests/test_widgets/test_fedmsgstats.py b/hubs/tests/test_widgets/test_fedmsgstats.py
index bec2363..0d7e79d 100644
--- a/hubs/tests/test_widgets/test_fedmsgstats.py
+++ b/hubs/tests/test_widgets/test_fedmsgstats.py
@@ -3,7 +3,6 @@ from __future__ import unicode_literals
 import json
 
 import hubs.tests.test_widgets
-from hubs.utils import to_str
 
 
 class TestFedmsgStats(hubs.tests.test_widgets.WidgetTest):
@@ -13,7 +12,7 @@ class TestFedmsgStats(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance('ralph', self.plugin)
         response = self.app.get('/ralph/%i/json/' % widget.idx)
         assert response.status_code == 200, response.status_code
-        data = json.loads(to_str(response.data))
+        data = json.loads(response.get_data(as_text=True))
         del data['data']['widget_url']
         self.assertDictEqual(data['data'], {
             u'edit_url': u'/ralph/37/edit',
diff --git a/hubs/tests/test_widgets/test_library.py b/hubs/tests/test_widgets/test_library.py
index 68f2598..9182b56 100644
--- a/hubs/tests/test_widgets/test_library.py
+++ b/hubs/tests/test_widgets/test_library.py
@@ -4,7 +4,6 @@ import json
 
 import hubs.tests.test_widgets
 import hubs.models
-from hubs.utils import to_str
 
 class TestLibrary(hubs.tests.test_widgets.WidgetTest):
     plugin = 'library'  # The name in hubs.widgets.registry
@@ -13,7 +12,7 @@ class TestLibrary(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance('ralph', self.plugin)
         response = self.app.get('/ralph/%i/json/' % widget.idx)
         self.assertEqual(response.status_code, 200)
-        data = json.loads(to_str(response.data))
+        data = json.loads(response.get_data(as_text=True))
         expected_dict = {
             "config": {
                 "urls": "ralph/"
diff --git a/hubs/tests/test_widgets/test_meetings.py b/hubs/tests/test_widgets/test_meetings.py
index d3b3f09..08ea046 100644
--- a/hubs/tests/test_widgets/test_meetings.py
+++ b/hubs/tests/test_widgets/test_meetings.py
@@ -3,7 +3,6 @@ from __future__ import unicode_literals
 import json
 
 import hubs.tests.test_widgets
-from hubs.utils import to_str
 
 
 class TestMeetings(hubs.tests.test_widgets.WidgetTest):
@@ -14,7 +13,7 @@ class TestMeetings(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance(team, self.plugin)
         response = self.app.get('/%s/%i/json/' % (team, widget.idx))
         self.assertEqual(200, response.status_code)
-        data = json.loads(to_str(response.data))
+        data = json.loads(response.get_data(as_text=True))
         calendar_name = data['data']['calendar']
         self.assertIn(team, calendar_name)
 
@@ -23,6 +22,6 @@ class TestMeetings(hubs.tests.test_widgets.WidgetTest):
         widget = self.widget_instance(team, self.plugin)
         response = self.app.get('/%s/%i/' % (team, widget.idx))
         self.assertEqual(200, response.status_code)
-        self.assertIn('i18n', to_str(response.data))
+        self.assertIn('i18n', response.get_data(as_text=True))
         self.assertIn('<strong>Request A New Meeting</strong>',
-                      to_str(response.data))
+                      response.get_data(as_text=True))
diff --git a/hubs/utils.py b/hubs/utils.py
index c858271..f1ecd92 100755
--- a/hubs/utils.py
+++ b/hubs/utils.py
@@ -81,14 +81,3 @@ def _github_results(url, auth):
                     part.split('; ')[0][1:-1],
                 ) for part in field.split(', ')
             ])
-
-
-def to_str(data):
-    """
-    A Python3-compatible way to return a string from a string or a UTF8-encoded
-    bytestring. Werkzeug responses are bytestrings on Python 3.
-    """
-    if isinstance(data, six.binary_type):
-        return data.decode('utf-8')
-    else:
-        return data

