[PATCH 8/9] py3: Resolve unicode issues
Stephen Finucane
stephen.finucane at intel.com
Mon Nov 30 09:10:49 AEDT 2015
Python 3 is unicode only. While many of the issues with unicode, such
as the now invalid 'u' prefix, have already been resolved, there are a
few more issues.
Many of these issues are related to HTTPResponse.content, which returns
bytes and needs to be "decoded" in order to perform actions like
concatenation with str objects (unicode). Where possible, make use of
assertContains, per the Django documentation (http://bit.ly/1lRDYie),
else fall back to including a 'decode' statement.
Signed-off-by: Stephen Finucane <stephen.finucane at intel.com>
---
patchwork/bin/parsemail.py | 4 +++-
patchwork/parser.py | 2 +-
patchwork/tests/test_bundles.py | 4 ++--
patchwork/tests/test_filters.py | 4 ++--
patchwork/tests/test_list.py | 2 +-
patchwork/tests/test_mail_settings.py | 27 +++++++++++++--------------
patchwork/tests/test_mboxviews.py | 4 ++--
patchwork/tests/test_person.py | 6 +++---
patchwork/views/patch.py | 7 ++++++-
9 files changed, 33 insertions(+), 27 deletions(-)
diff --git a/patchwork/bin/parsemail.py b/patchwork/bin/parsemail.py
index 2bc080c..43d733f 100755
--- a/patchwork/bin/parsemail.py
+++ b/patchwork/bin/parsemail.py
@@ -68,7 +68,9 @@ def clean_header(header):
(frag_str, frag_encoding) = fragment
if frag_encoding:
return frag_str.decode(frag_encoding)
- return frag_str.decode()
+ elif isinstance(frag_str, six.binary_type): # python 2
+ return frag_str.decode()
+ return frag_str
fragments = list(map(decode, decode_header(header)))
diff --git a/patchwork/parser.py b/patchwork/parser.py
index 84f704a..b65e50c 100644
--- a/patchwork/parser.py
+++ b/patchwork/parser.py
@@ -230,7 +230,7 @@ def hash_patch(str):
# other lines are ignored
continue
- hash.update(line.encode('utf-8') + '\n')
+ hash.update((line + '\n').encode('utf-8'))
return hash
diff --git a/patchwork/tests/test_bundles.py b/patchwork/tests/test_bundles.py
index 4b982a5..d5d5d2f 100644
--- a/patchwork/tests/test_bundles.py
+++ b/patchwork/tests/test_bundles.py
@@ -109,7 +109,7 @@ class BundleViewTest(BundleTestBase):
pos = 0
for patch in self.patches:
- next_pos = response.content.find(patch.name)
+ next_pos = response.content.decode().find(patch.name)
# ensure that this patch is after the previous
self.failUnless(next_pos > pos)
pos = next_pos
@@ -126,7 +126,7 @@ class BundleViewTest(BundleTestBase):
response = self.client.get(bundle_url(self.bundle))
pos = len(response.content)
for patch in self.patches:
- next_pos = response.content.find(patch.name)
+ next_pos = response.content.decode().find(patch.name)
# ensure that this patch is now *before* the previous
self.failUnless(next_pos < pos)
pos = next_pos
diff --git a/patchwork/tests/test_filters.py b/patchwork/tests/test_filters.py
index ae1ff7c..c92146b 100644
--- a/patchwork/tests/test_filters.py
+++ b/patchwork/tests/test_filters.py
@@ -35,8 +35,8 @@ class FilterQueryStringTest(TestCase):
url = '/project/%s/list/?submitter=a%%26b=c' % project.linkname
response = self.client.get(url)
self.failUnlessEqual(response.status_code, 200)
- self.failIf('submitter=a&b=c' in response.content)
- self.failIf('submitter=a&b=c' in response.content)
+ self.assertNotContains(response, 'submitter=a&b=c')
+ self.assertNotContains(response, 'submitter=a&b=c')
def testUTF8QSHandling(self):
"""test that non-ascii characters can be handled by the filter
diff --git a/patchwork/tests/test_list.py b/patchwork/tests/test_list.py
index 0f23d13..62ff121 100644
--- a/patchwork/tests/test_list.py
+++ b/patchwork/tests/test_list.py
@@ -77,7 +77,7 @@ class PatchOrderTest(TestCase):
def _extract_patch_ids(self, response):
id_re = re.compile('<tr id="patch_row:(\d+)"')
- ids = [ int(m.group(1)) for m in id_re.finditer(response.content) ]
+ ids = [ int(m.group(1)) for m in id_re.finditer(response.content.decode()) ]
return ids
def _test_sequence(self, response, test_fn):
diff --git a/patchwork/tests/test_mail_settings.py b/patchwork/tests/test_mail_settings.py
index a3956a9..39133f9 100644
--- a/patchwork/tests/test_mail_settings.py
+++ b/patchwork/tests/test_mail_settings.py
@@ -63,9 +63,9 @@ class MailSettingsTest(TestCase):
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'patchwork/mail-settings.html')
self.assertEquals(response.context['is_optout'], False)
- self.assertTrue('<strong>may</strong>' in response.content)
+ self.assertContains(response, '<strong>may</strong>')
optout_url = reverse('patchwork.views.mail.optout')
- self.assertTrue(('action="%s"' % optout_url) in response.content)
+ self.assertContains(response, ('action="%s"' % optout_url))
def testMailSettingsPOSTOptedOut(self):
email = u'foo at example.com'
@@ -74,9 +74,9 @@ class MailSettingsTest(TestCase):
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'patchwork/mail-settings.html')
self.assertEquals(response.context['is_optout'], True)
- self.assertTrue('<strong>may not</strong>' in response.content)
+ self.assertContains(response, '<strong>may not</strong>')
optin_url = reverse('patchwork.views.mail.optin')
- self.assertTrue(('action="%s"' % optin_url) in response.content)
+ self.assertContains(response, ('action="%s"' % optin_url))
class OptoutRequestTest(TestCase):
@@ -98,7 +98,7 @@ class OptoutRequestTest(TestCase):
# check confirmation page
self.assertEquals(response.status_code, 200)
self.assertEquals(response.context['confirmation'], conf)
- self.assertTrue(email in response.content)
+ self.assertContains(response, email)
# check email
url = reverse('patchwork.views.confirm', kwargs = {'key': conf.key})
@@ -140,7 +140,7 @@ class OptoutTest(TestCase):
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'patchwork/optout.html')
- self.assertTrue(self.email in response.content)
+ self.assertContains(response, self.email)
# check that we've got an optout in the list
self.assertEquals(EmailOptout.objects.count(), 1)
@@ -178,7 +178,7 @@ class OptinRequestTest(TestCase):
# check confirmation page
self.assertEquals(response.status_code, 200)
self.assertEquals(response.context['confirmation'], conf)
- self.assertTrue(self.email in response.content)
+ self.assertContains(response, self.email)
# check email
url = reverse('patchwork.views.confirm', kwargs = {'key': conf.key})
@@ -221,7 +221,7 @@ class OptinTest(TestCase):
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'patchwork/optin.html')
- self.assertTrue(self.email in response.content)
+ self.assertContains(response, self.email)
# check that there's no optout remaining
self.assertEquals(EmailOptout.objects.count(), 0)
@@ -243,7 +243,7 @@ class OptinWithoutOptoutTest(TestCase):
# check for an error message
self.assertEquals(response.status_code, 200)
self.assertTrue(bool(response.context['error']))
- self.assertTrue('not on the patchwork opt-out list' in response.content)
+ self.assertContains(response, 'not on the patchwork opt-out list')
class UserProfileOptoutFormTest(TestCase):
"""Test that the correct optin/optout forms appear on the user profile
@@ -270,23 +270,22 @@ class UserProfileOptoutFormTest(TestCase):
form_re = self._form_re(self.optout_url, self.user.email)
response = self.client.get(self.url)
self.assertEquals(response.status_code, 200)
- self.assertTrue(form_re.search(response.content) is not None)
+ self.assertTrue(form_re.search(response.content.decode()) is not None)
def testMainEmailOptinForm(self):
EmailOptout(email = self.user.email).save()
form_re = self._form_re(self.optin_url, self.user.email)
response = self.client.get(self.url)
self.assertEquals(response.status_code, 200)
- self.assertTrue(form_re.search(response.content) is not None)
+ self.assertTrue(form_re.search(response.content.decode()) is not None)
def testSecondaryEmailOptoutForm(self):
p = Person(email = self.secondary_email, user = self.user)
p.save()
-
form_re = self._form_re(self.optout_url, p.email)
response = self.client.get(self.url)
self.assertEquals(response.status_code, 200)
- self.assertTrue(form_re.search(response.content) is not None)
+ self.assertTrue(form_re.search(response.content.decode()) is not None)
def testSecondaryEmailOptinForm(self):
p = Person(email = self.secondary_email, user = self.user)
@@ -296,4 +295,4 @@ class UserProfileOptoutFormTest(TestCase):
form_re = self._form_re(self.optin_url, p.email)
response = self.client.get(self.url)
self.assertEquals(response.status_code, 200)
- self.assertTrue(form_re.search(response.content) is not None)
+ self.assertTrue(form_re.search(response.content.decode()) is not None)
diff --git a/patchwork/tests/test_mboxviews.py b/patchwork/tests/test_mboxviews.py
index 8c98351..37bab74 100644
--- a/patchwork/tests/test_mboxviews.py
+++ b/patchwork/tests/test_mboxviews.py
@@ -173,7 +173,7 @@ class MboxDateHeaderTest(TestCase):
def testDateHeader(self):
response = self.client.get('/patch/%d/mbox/' % self.patch.id)
- mail = email.message_from_string(response.content)
+ mail = email.message_from_string(response.content.decode())
mail_date = dateutil.parser.parse(mail['Date'])
# patch dates are all in UTC
patch_date = self.patch.date.replace(tzinfo=dateutil.tz.tzutc(),
@@ -190,7 +190,7 @@ class MboxDateHeaderTest(TestCase):
self.patch.save()
response = self.client.get('/patch/%d/mbox/' % self.patch.id)
- mail = email.message_from_string(response.content)
+ mail = email.message_from_string(response.content.decode())
mail_date = dateutil.parser.parse(mail['Date'])
self.assertEqual(mail_date, date)
diff --git a/patchwork/tests/test_person.py b/patchwork/tests/test_person.py
index ef35da6..9b91115 100644
--- a/patchwork/tests/test_person.py
+++ b/patchwork/tests/test_person.py
@@ -39,14 +39,14 @@ class SubmitterCompletionTest(TestCase):
def testNameComplete(self):
response = self.client.get('/submitter/', {'q': 'name'})
self.assertEquals(response.status_code, 200)
- data = json.loads(response.content)
+ data = json.loads(response.content.decode())
self.assertEquals(len(data), 1)
self.assertEquals(data[0]['name'], 'Test Name')
def testEmailComplete(self):
response = self.client.get('/submitter/', {'q': 'test2'})
self.assertEquals(response.status_code, 200)
- data = json.loads(response.content)
+ data = json.loads(response.content.decode())
self.assertEquals(len(data), 1)
self.assertEquals(data[0]['email'], 'test2 at example.com')
@@ -56,5 +56,5 @@ class SubmitterCompletionTest(TestCase):
person.save()
response = self.client.get('/submitter/', {'q': 'test', 'l': 5})
self.assertEquals(response.status_code, 200)
- data = json.loads(response.content)
+ data = json.loads(response.content.decode())
self.assertEquals(len(data), 5)
diff --git a/patchwork/views/patch.py b/patchwork/views/patch.py
index cb017fb..7abb22f 100644
--- a/patchwork/views/patch.py
+++ b/patchwork/views/patch.py
@@ -21,6 +21,7 @@ from __future__ import absolute_import
from django.http import HttpResponse, HttpResponseForbidden
from django.shortcuts import render_to_response, get_object_or_404
+from django.utils import six
from patchwork.forms import PatchForm, CreateBundleForm
from patchwork.models import Patch, Project, Bundle
@@ -96,7 +97,11 @@ def content(request, patch_id):
def mbox(request, patch_id):
patch = get_object_or_404(Patch, id=patch_id)
response = HttpResponse(content_type="text/plain")
- response.write(patch_to_mbox(patch).as_string(True))
+ # NOTE(stephenfin) http://stackoverflow.com/a/28584090/613428
+ if six.PY3:
+ response.write(patch_to_mbox(patch).as_bytes(True).decode())
+ else:
+ response.write(patch_to_mbox(patch).as_string(True))
response['Content-Disposition'] = 'attachment; filename=' + \
patch.filename().replace(';', '').replace('\n', '')
return response
--
2.0.0
More information about the Patchwork
mailing list