mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 03:41:05 -05:00
Drop Python 2 and Django 1.11 support
Minimum supported versions are now Django 2.0, Python 3.5. This touches a lot of code, to: * Remove obsolete portability code and workarounds (six, backports of email parsers, test utils, etc.) * Use Python 3 syntax (class defs, raise ... from, etc.) * Correct inheritance for mixin classes * Fix outdated docs content and links * Suppress Python 3 "unclosed SSLSocket" ResourceWarnings that are beyond our control (in integration tests due to boto3, python-sparkpost)
This commit is contained in:
@@ -4,22 +4,11 @@ import base64
|
||||
import copy
|
||||
import pickle
|
||||
from email.mime.image import MIMEImage
|
||||
from unittest import skipIf
|
||||
|
||||
import six
|
||||
from django.http import QueryDict
|
||||
from django.test import SimpleTestCase, RequestFactory, override_settings
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
try:
|
||||
from django.utils.text import format_lazy # Django >= 1.11
|
||||
except ImportError:
|
||||
format_lazy = None
|
||||
|
||||
try:
|
||||
from django.utils.translation import string_concat # Django < 2.1
|
||||
except ImportError:
|
||||
string_concat = None
|
||||
from django.utils.text import format_lazy
|
||||
from django.utils.translation import gettext_lazy
|
||||
|
||||
from anymail.exceptions import AnymailInvalidAddress, _LazyError
|
||||
from anymail.utils import (
|
||||
@@ -66,11 +55,11 @@ class ParseAddressListTests(SimpleTestCase):
|
||||
self.assertEqual(parsed.address, 'Display Name <test@example.com>')
|
||||
|
||||
def test_unicode_display_name(self):
|
||||
parsed_list = parse_address_list([u'"Unicode \N{HEAVY BLACK HEART}" <test@example.com>'])
|
||||
parsed_list = parse_address_list(['"Unicode \N{HEAVY BLACK HEART}" <test@example.com>'])
|
||||
self.assertEqual(len(parsed_list), 1)
|
||||
parsed = parsed_list[0]
|
||||
self.assertEqual(parsed.addr_spec, "test@example.com")
|
||||
self.assertEqual(parsed.display_name, u"Unicode \N{HEAVY BLACK HEART}")
|
||||
self.assertEqual(parsed.display_name, "Unicode \N{HEAVY BLACK HEART}")
|
||||
# formatted display-name automatically shifts to quoted-printable/base64 for non-ascii chars:
|
||||
self.assertEqual(parsed.address, '=?utf-8?b?VW5pY29kZSDinaQ=?= <test@example.com>')
|
||||
|
||||
@@ -83,13 +72,13 @@ class ParseAddressListTests(SimpleTestCase):
|
||||
parse_address_list(['Display Name, Inc. <test@example.com>'])
|
||||
|
||||
def test_idn(self):
|
||||
parsed_list = parse_address_list([u"idn@\N{ENVELOPE}.example.com"])
|
||||
parsed_list = parse_address_list(["idn@\N{ENVELOPE}.example.com"])
|
||||
self.assertEqual(len(parsed_list), 1)
|
||||
parsed = parsed_list[0]
|
||||
self.assertEqual(parsed.addr_spec, u"idn@\N{ENVELOPE}.example.com")
|
||||
self.assertEqual(parsed.addr_spec, "idn@\N{ENVELOPE}.example.com")
|
||||
self.assertEqual(parsed.address, "idn@xn--4bi.example.com") # punycode-encoded domain
|
||||
self.assertEqual(parsed.username, "idn")
|
||||
self.assertEqual(parsed.domain, u"\N{ENVELOPE}.example.com")
|
||||
self.assertEqual(parsed.domain, "\N{ENVELOPE}.example.com")
|
||||
|
||||
def test_none_address(self):
|
||||
# used for, e.g., telling Mandrill to use template default from_email
|
||||
@@ -139,10 +128,9 @@ class ParseAddressListTests(SimpleTestCase):
|
||||
parse_address_list(['"Display Name"', '<valid@example.com>'])
|
||||
|
||||
def test_invalid_with_unicode(self):
|
||||
# (assertRaisesMessage can't handle unicode in Python 2)
|
||||
with self.assertRaises(AnymailInvalidAddress) as cm:
|
||||
parse_address_list([u"\N{ENVELOPE}"])
|
||||
self.assertIn(u"Invalid email address '\N{ENVELOPE}'", six.text_type(cm.exception))
|
||||
with self.assertRaisesMessage(AnymailInvalidAddress,
|
||||
"Invalid email address '\N{ENVELOPE}'"):
|
||||
parse_address_list(["\N{ENVELOPE}"])
|
||||
|
||||
def test_single_string(self):
|
||||
# bare strings are used by the from_email parsing in BasePayload
|
||||
@@ -151,12 +139,12 @@ class ParseAddressListTests(SimpleTestCase):
|
||||
self.assertEqual(parsed_list[0].addr_spec, "one@example.com")
|
||||
|
||||
def test_lazy_strings(self):
|
||||
parsed_list = parse_address_list([ugettext_lazy('"Example, Inc." <one@example.com>')])
|
||||
parsed_list = parse_address_list([gettext_lazy('"Example, Inc." <one@example.com>')])
|
||||
self.assertEqual(len(parsed_list), 1)
|
||||
self.assertEqual(parsed_list[0].display_name, "Example, Inc.")
|
||||
self.assertEqual(parsed_list[0].addr_spec, "one@example.com")
|
||||
|
||||
parsed_list = parse_address_list(ugettext_lazy("one@example.com"))
|
||||
parsed_list = parse_address_list(gettext_lazy("one@example.com"))
|
||||
self.assertEqual(len(parsed_list), 1)
|
||||
self.assertEqual(parsed_list[0].display_name, "")
|
||||
self.assertEqual(parsed_list[0].addr_spec, "one@example.com")
|
||||
@@ -221,47 +209,38 @@ class LazyCoercionTests(SimpleTestCase):
|
||||
"""Test utils.is_lazy and force_non_lazy*"""
|
||||
|
||||
def test_is_lazy(self):
|
||||
self.assertTrue(is_lazy(ugettext_lazy("lazy string is lazy")))
|
||||
self.assertTrue(is_lazy(gettext_lazy("lazy string is lazy")))
|
||||
|
||||
def test_not_lazy(self):
|
||||
self.assertFalse(is_lazy(u"text not lazy"))
|
||||
self.assertFalse(is_lazy("text not lazy"))
|
||||
self.assertFalse(is_lazy(b"bytes not lazy"))
|
||||
self.assertFalse(is_lazy(None))
|
||||
self.assertFalse(is_lazy({'dict': "not lazy"}))
|
||||
self.assertFalse(is_lazy(["list", "not lazy"]))
|
||||
self.assertFalse(is_lazy(object()))
|
||||
self.assertFalse(is_lazy([ugettext_lazy("doesn't recurse")]))
|
||||
self.assertFalse(is_lazy([gettext_lazy("doesn't recurse")]))
|
||||
|
||||
def test_force_lazy(self):
|
||||
result = force_non_lazy(ugettext_lazy(u"text"))
|
||||
self.assertIsInstance(result, six.text_type)
|
||||
self.assertEqual(result, u"text")
|
||||
result = force_non_lazy(gettext_lazy("text"))
|
||||
self.assertIsInstance(result, str)
|
||||
self.assertEqual(result, "text")
|
||||
|
||||
@skipIf(string_concat is None, "string_concat not in this Django version")
|
||||
def test_force_concat(self):
|
||||
self.assertTrue(is_lazy(string_concat(ugettext_lazy("concatenation"),
|
||||
ugettext_lazy("is lazy"))))
|
||||
result = force_non_lazy(string_concat(ugettext_lazy(u"text"), ugettext_lazy("concat")))
|
||||
self.assertIsInstance(result, six.text_type)
|
||||
self.assertEqual(result, u"textconcat")
|
||||
|
||||
@skipIf(format_lazy is None, "format_lazy not in this Django version")
|
||||
def test_format_lazy(self):
|
||||
self.assertTrue(is_lazy(format_lazy("{0}{1}",
|
||||
ugettext_lazy("concatenation"), ugettext_lazy("is lazy"))))
|
||||
gettext_lazy("concatenation"), gettext_lazy("is lazy"))))
|
||||
result = force_non_lazy(format_lazy("{first}/{second}",
|
||||
first=ugettext_lazy(u"text"), second=ugettext_lazy("format")))
|
||||
self.assertIsInstance(result, six.text_type)
|
||||
self.assertEqual(result, u"text/format")
|
||||
first=gettext_lazy("text"), second=gettext_lazy("format")))
|
||||
self.assertIsInstance(result, str)
|
||||
self.assertEqual(result, "text/format")
|
||||
|
||||
def test_force_string(self):
|
||||
result = force_non_lazy(u"text")
|
||||
self.assertIsInstance(result, six.text_type)
|
||||
self.assertEqual(result, u"text")
|
||||
result = force_non_lazy("text")
|
||||
self.assertIsInstance(result, str)
|
||||
self.assertEqual(result, "text")
|
||||
|
||||
def test_force_bytes(self):
|
||||
result = force_non_lazy(b"bytes \xFE")
|
||||
self.assertIsInstance(result, six.binary_type)
|
||||
self.assertIsInstance(result, bytes)
|
||||
self.assertEqual(result, b"bytes \xFE")
|
||||
|
||||
def test_force_none(self):
|
||||
@@ -269,16 +248,16 @@ class LazyCoercionTests(SimpleTestCase):
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_force_dict(self):
|
||||
result = force_non_lazy_dict({'a': 1, 'b': ugettext_lazy(u"b"),
|
||||
'c': {'c1': ugettext_lazy(u"c1")}})
|
||||
self.assertEqual(result, {'a': 1, 'b': u"b", 'c': {'c1': u"c1"}})
|
||||
self.assertIsInstance(result['b'], six.text_type)
|
||||
self.assertIsInstance(result['c']['c1'], six.text_type)
|
||||
result = force_non_lazy_dict({'a': 1, 'b': gettext_lazy("b"),
|
||||
'c': {'c1': gettext_lazy("c1")}})
|
||||
self.assertEqual(result, {'a': 1, 'b': "b", 'c': {'c1': "c1"}})
|
||||
self.assertIsInstance(result['b'], str)
|
||||
self.assertIsInstance(result['c']['c1'], str)
|
||||
|
||||
def test_force_list(self):
|
||||
result = force_non_lazy_list([0, ugettext_lazy(u"b"), u"c"])
|
||||
self.assertEqual(result, [0, u"b", u"c"]) # coerced to list
|
||||
self.assertIsInstance(result[1], six.text_type)
|
||||
result = force_non_lazy_list([0, gettext_lazy("b"), "c"])
|
||||
self.assertEqual(result, [0, "b", "c"]) # coerced to list
|
||||
self.assertIsInstance(result[1], str)
|
||||
|
||||
|
||||
class UpdateDeepTests(SimpleTestCase):
|
||||
@@ -313,7 +292,7 @@ class RequestUtilsTests(SimpleTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.request_factory = RequestFactory()
|
||||
super(RequestUtilsTests, self).setUp()
|
||||
super().setUp()
|
||||
|
||||
@staticmethod
|
||||
def basic_auth(username, password):
|
||||
|
||||
Reference in New Issue
Block a user