Browse Source

[py3] Fixed mail tests with Python 3

Claude Paroz 12 years ago
parent
commit
751774c29f
2 changed files with 46 additions and 42 deletions
  1. 17 17
      django/core/mail/message.py
  2. 29 25
      tests/regressiontests/mail/tests.py

+ 17 - 17
django/core/mail/message.py

@@ -11,11 +11,10 @@ from email.mime.multipart import MIMEMultipart
 from email.mime.base import MIMEBase
 from email.header import Header
 from email.utils import formatdate, getaddresses, formataddr, parseaddr
-from io import BytesIO
 
 from django.conf import settings
 from django.core.mail.utils import DNS_NAME
-from django.utils.encoding import smart_bytes, force_text
+from django.utils.encoding import force_text
 from django.utils import six
 
 
@@ -83,34 +82,34 @@ def forbid_multi_line_headers(name, val, encoding):
     if '\n' in val or '\r' in val:
         raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
     try:
-        val = val.encode('ascii')
+        val.encode('ascii')
     except UnicodeEncodeError:
         if name.lower() in ADDRESS_HEADERS:
             val = ', '.join(sanitize_address(addr, encoding)
                 for addr in getaddresses((val,)))
         else:
-            val = str(Header(val, encoding))
+            val = Header(val, encoding).encode()
     else:
         if name.lower() == 'subject':
-            val = Header(val)
-    return smart_bytes(name), val
+            val = Header(val).encode()
+    return str(name), val
 
 
 def sanitize_address(addr, encoding):
     if isinstance(addr, six.string_types):
         addr = parseaddr(force_text(addr))
     nm, addr = addr
-    nm = str(Header(nm, encoding))
+    nm = Header(nm, encoding).encode()
     try:
-        addr = addr.encode('ascii')
+        addr.encode('ascii')
     except UnicodeEncodeError:  # IDN
         if '@' in addr:
             localpart, domain = addr.split('@', 1)
             localpart = str(Header(localpart, encoding))
-            domain = domain.encode('idna')
+            domain = domain.encode('idna').decode('ascii')
             addr = '@'.join([localpart, domain])
         else:
-            addr = str(Header(addr, encoding))
+            addr = Header(addr, encoding).encode()
     return formataddr((nm, addr))
 
 
@@ -132,7 +131,7 @@ class SafeMIMEText(MIMEText):
         This overrides the default as_string() implementation to not mangle
         lines that begin with 'From '. See bug #13433 for details.
         """
-        fp = BytesIO()
+        fp = six.StringIO()
         g = Generator(fp, mangle_from_ = False)
         g.flatten(self, unixfrom=unixfrom)
         return fp.getvalue()
@@ -156,7 +155,7 @@ class SafeMIMEMultipart(MIMEMultipart):
         This overrides the default as_string() implementation to not mangle
         lines that begin with 'From '. See bug #13433 for details.
         """
-        fp = BytesIO()
+        fp = six.StringIO()
         g = Generator(fp, mangle_from_ = False)
         g.flatten(self, unixfrom=unixfrom)
         return fp.getvalue()
@@ -210,8 +209,7 @@ class EmailMessage(object):
 
     def message(self):
         encoding = self.encoding or settings.DEFAULT_CHARSET
-        msg = SafeMIMEText(smart_bytes(self.body, encoding),
-                           self.content_subtype, encoding)
+        msg = SafeMIMEText(self.body, self.content_subtype, encoding)
         msg = self._create_message(msg)
         msg['Subject'] = self.subject
         msg['From'] = self.extra_headers.get('From', self.from_email)
@@ -293,7 +291,7 @@ class EmailMessage(object):
         basetype, subtype = mimetype.split('/', 1)
         if basetype == 'text':
             encoding = self.encoding or settings.DEFAULT_CHARSET
-            attachment = SafeMIMEText(smart_bytes(content, encoding), subtype, encoding)
+            attachment = SafeMIMEText(content, subtype, encoding)
         else:
             # Encode non-text attachments with base64.
             attachment = MIMEBase(basetype, subtype)
@@ -313,9 +311,11 @@ class EmailMessage(object):
         attachment = self._create_mime_attachment(content, mimetype)
         if filename:
             try:
-                filename = filename.encode('ascii')
+                filename.encode('ascii')
             except UnicodeEncodeError:
-                filename = ('utf-8', '', filename.encode('utf-8'))
+                if not six.PY3:
+                    filename = filename.encode('utf-8')
+                filename = ('utf-8', '', filename)
             attachment.add_header('Content-Disposition', 'attachment',
                                   filename=filename)
         return attachment

+ 29 - 25
tests/regressiontests/mail/tests.py

@@ -17,7 +17,7 @@ from django.core.mail.backends import console, dummy, locmem, filebased, smtp
 from django.core.mail.message import BadHeaderError
 from django.test import TestCase
 from django.test.utils import override_settings
-from django.utils.six import StringIO
+from django.utils.six import PY3, StringIO
 from django.utils.translation import ugettext_lazy
 
 
@@ -29,7 +29,7 @@ class MailTests(TestCase):
     def test_ascii(self):
         email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'])
         message = email.message()
-        self.assertEqual(message['Subject'].encode(), 'Subject')
+        self.assertEqual(message['Subject'], 'Subject')
         self.assertEqual(message.get_payload(), 'Content')
         self.assertEqual(message['From'], 'from@example.com')
         self.assertEqual(message['To'], 'to@example.com')
@@ -37,7 +37,7 @@ class MailTests(TestCase):
     def test_multiple_recipients(self):
         email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com', 'other@example.com'])
         message = email.message()
-        self.assertEqual(message['Subject'].encode(), 'Subject')
+        self.assertEqual(message['Subject'], 'Subject')
         self.assertEqual(message.get_payload(), 'Content')
         self.assertEqual(message['From'], 'from@example.com')
         self.assertEqual(message['To'], 'to@example.com, other@example.com')
@@ -77,9 +77,10 @@ class MailTests(TestCase):
         """
         Test for space continuation character in long (ascii) subject headers (#7747)
         """
-        email = EmailMessage('Long subject lines that get wrapped should use a space continuation character to get expected behavior in Outlook and Thunderbird', 'Content', 'from@example.com', ['to@example.com'])
+        email = EmailMessage('Long subject lines that get wrapped should contain a space continuation character to get expected behavior in Outlook and Thunderbird', 'Content', 'from@example.com', ['to@example.com'])
         message = email.message()
-        self.assertEqual(message['Subject'], 'Long subject lines that get wrapped should use a space continuation\n character to get expected behavior in Outlook and Thunderbird')
+        # Note that in Python 3, maximum line length has increased from 76 to 78
+        self.assertEqual(message['Subject'].encode(), b'Long subject lines that get wrapped should contain a space continuation\n character to get expected behavior in Outlook and Thunderbird')
 
     def test_message_header_overrides(self):
         """
@@ -88,7 +89,7 @@ class MailTests(TestCase):
         """
         headers = {"date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
         email = EmailMessage('subject', 'content', 'from@example.com', ['to@example.com'], headers=headers)
-        self.assertEqual(email.message().as_string(), b'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nSubject: subject\nFrom: from@example.com\nTo: to@example.com\ndate: Fri, 09 Nov 2001 01:08:47 -0000\nMessage-ID: foo\n\ncontent')
+        self.assertEqual(email.message().as_string(), 'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nSubject: subject\nFrom: from@example.com\nTo: to@example.com\ndate: Fri, 09 Nov 2001 01:08:47 -0000\nMessage-ID: foo\n\ncontent')
 
     def test_from_header(self):
         """
@@ -160,7 +161,7 @@ class MailTests(TestCase):
         msg.attach_alternative(html_content, "text/html")
         msg.encoding = 'iso-8859-1'
         self.assertEqual(msg.message()['To'], '=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>')
-        self.assertEqual(msg.message()['Subject'].encode(), '=?iso-8859-1?q?Message_from_Firstname_S=FCrname?=')
+        self.assertEqual(msg.message()['Subject'], '=?iso-8859-1?q?Message_from_Firstname_S=FCrname?=')
 
     def test_encoding(self):
         """
@@ -170,7 +171,7 @@ class MailTests(TestCase):
         email = EmailMessage('Subject', 'Firstname Sürname is a great guy.', 'from@example.com', ['other@example.com'])
         email.encoding = 'iso-8859-1'
         message = email.message()
-        self.assertTrue(message.as_string().startswith(b'Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: other@example.com'))
+        self.assertTrue(message.as_string().startswith('Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: other@example.com'))
         self.assertEqual(message.get_payload(), 'Firstname S=FCrname is a great guy.')
 
         # Make sure MIME attachments also works correctly with other encodings than utf-8
@@ -179,8 +180,8 @@ class MailTests(TestCase):
         msg = EmailMultiAlternatives('Subject', text_content, 'from@example.com', ['to@example.com'])
         msg.encoding = 'iso-8859-1'
         msg.attach_alternative(html_content, "text/html")
-        self.assertEqual(msg.message().get_payload(0).as_string(), b'Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\nFirstname S=FCrname is a great guy.')
-        self.assertEqual(msg.message().get_payload(1).as_string(), b'Content-Type: text/html; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\n<p>Firstname S=FCrname is a <strong>great</strong> guy.</p>')
+        self.assertEqual(msg.message().get_payload(0).as_string(), 'Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\nFirstname S=FCrname is a great guy.')
+        self.assertEqual(msg.message().get_payload(1).as_string(), 'Content-Type: text/html; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\n<p>Firstname S=FCrname is a <strong>great</strong> guy.</p>')
 
     def test_attachments(self):
         """Regression test for #9367"""
@@ -291,31 +292,31 @@ class MailTests(TestCase):
         # Regression for #13433 - Make sure that EmailMessage doesn't mangle
         # 'From ' in message body.
         email = EmailMessage('Subject', 'From the future', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
-        self.assertFalse(b'>From the future' in email.message().as_string())
+        self.assertFalse('>From the future' in email.message().as_string())
 
     def test_dont_base64_encode(self):
         # Ticket #3472
         # Shouldn't use Base64 encoding at all
         msg = EmailMessage('Subject', 'UTF-8 encoded body', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
-        self.assertFalse(b'Content-Transfer-Encoding: base64' in msg.message().as_string())
+        self.assertFalse('Content-Transfer-Encoding: base64' in msg.message().as_string())
 
         # Ticket #11212
         # Shouldn't use quoted printable, should detect it can represent content with 7 bit data
         msg = EmailMessage('Subject', 'Body with only ASCII characters.', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
         s = msg.message().as_string()
-        self.assertFalse(b'Content-Transfer-Encoding: quoted-printable' in s)
-        self.assertTrue(b'Content-Transfer-Encoding: 7bit' in s)
+        self.assertFalse('Content-Transfer-Encoding: quoted-printable' in s)
+        self.assertTrue('Content-Transfer-Encoding: 7bit' in s)
 
         # Shouldn't use quoted printable, should detect it can represent content with 8 bit data
         msg = EmailMessage('Subject', 'Body with latin characters: àáä.', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
         s = msg.message().as_string()
-        self.assertFalse(b'Content-Transfer-Encoding: quoted-printable' in s)
-        self.assertTrue(b'Content-Transfer-Encoding: 8bit' in s)
+        self.assertFalse(str('Content-Transfer-Encoding: quoted-printable') in s)
+        self.assertTrue(str('Content-Transfer-Encoding: 8bit') in s)
 
         msg = EmailMessage('Subject', 'Body with non latin characters: А Б В Г Д Е Ж Ѕ З И І К Л М Н О П.', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
         s = msg.message().as_string()
-        self.assertFalse(b'Content-Transfer-Encoding: quoted-printable' in s)
-        self.assertTrue(b'Content-Transfer-Encoding: 8bit' in s)
+        self.assertFalse(str('Content-Transfer-Encoding: quoted-printable') in s)
+        self.assertTrue(str('Content-Transfer-Encoding: 8bit') in s)
 
 
 class BaseEmailBackendTests(object):
@@ -440,7 +441,7 @@ class BaseEmailBackendTests(object):
         email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'], cc=['cc@example.com'])
         mail.get_connection().send_messages([email])
         message = self.get_the_message()
-        self.assertStartsWith(message.as_string(), b'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nSubject: Subject\nFrom: from@example.com\nTo: to@example.com\nCc: cc@example.com\nDate: ')
+        self.assertStartsWith(message.as_string(), 'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nSubject: Subject\nFrom: from@example.com\nTo: to@example.com\nCc: cc@example.com\nDate: ')
 
     def test_idn_send(self):
         """
@@ -519,9 +520,9 @@ class FileBackendTests(BaseEmailBackendTests, TestCase):
     def get_mailbox_content(self):
         messages = []
         for filename in os.listdir(self.tmp_dir):
-            with open(os.path.join(self.tmp_dir, filename), 'rb') as fp:
-                session = fp.read().split(b'\n' + (b'-' * 79) + b'\n')
-            messages.extend(email.message_from_string(m) for m in session if m)
+            with open(os.path.join(self.tmp_dir, filename), 'r') as fp:
+                session = fp.read().split('\n' + ('-' * 79) + '\n')
+            messages.extend(email.message_from_string(str(m)) for m in session if m)
         return messages
 
     def test_file_sessions(self):
@@ -571,8 +572,8 @@ class ConsoleBackendTests(BaseEmailBackendTests, TestCase):
         self.stream = sys.stdout = StringIO()
 
     def get_mailbox_content(self):
-        messages = self.stream.getvalue().split(b'\n' + (b'-' * 79) + b'\n')
-        return [email.message_from_string(m) for m in messages if m]
+        messages = self.stream.getvalue().split('\n' + ('-' * 79) + '\n')
+        return [email.message_from_string(str(m)) for m in messages if m]
 
     def test_console_stream_kwarg(self):
         """
@@ -600,7 +601,10 @@ class FakeSMTPServer(smtpd.SMTPServer, threading.Thread):
 
     def process_message(self, peer, mailfrom, rcpttos, data):
         m = email.message_from_string(data)
-        maddr = email.Utils.parseaddr(m.get('from'))[1]
+        if PY3:
+            maddr = email.utils.parseaddr(m.get('from'))[1]
+        else:
+            maddr = email.Utils.parseaddr(m.get('from'))[1]
         if mailfrom != maddr:
             return "553 '%s' != '%s'" % (mailfrom, maddr)
         with self.sink_lock: