diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d464f7f..f992636 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,6 +25,18 @@ Release history ^^^^^^^^^^^^^^^ .. This extra heading level keeps the ToC from becoming unmanageably long +vNext +----- + +*Unreleased changes on master* + +Fixes +~~~~~ + +* Support using `AnymailMessage` with django-mailer and similar packages that pickle + messages. (See `#147`_. Thanks to `@ewingrj`_ for identifying the problem.) + + v6.0 ---- @@ -928,11 +940,13 @@ Features .. _#111: https://github.com/anymail/issues/111 .. _#112: https://github.com/anymail/issues/112 .. _#115: https://github.com/anymail/issues/115 +.. _#147: https://github.com/anymail/issues/147 .. _@ailionx: https://github.com/ailionx .. _@calvin: https://github.com/calvin .. _@costela: https://github.com/costela .. _@decibyte: https://github.com/decibyte +.. _@ewingrj: https://github.com/ewingrj .. _@janneThoft: https://github.com/janneThoft .. _@joshkersey: https://github.com/joshkersey .. _@Lekensteyn: https://github.com/Lekensteyn diff --git a/anymail/utils.py b/anymail/utils.py index 7a22d06..43f66bc 100644 --- a/anymail/utils.py +++ b/anymail/utils.py @@ -26,7 +26,7 @@ from .exceptions import AnymailConfigurationError, AnymailInvalidAddress BASIC_NUMERIC_TYPES = six.integer_types + (float,) # int, float, and (on Python 2) long -UNSET = object() # Used as non-None default value +UNSET = type('UNSET', (object,), {}) # Used as non-None default value def combine(*args): diff --git a/tests/test_utils.py b/tests/test_utils.py index bc6c8af..25f1d33 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,8 @@ # Tests for the anymail/utils.py module # (not to be confused with utilities for testing found in in tests/utils.py) import base64 +import copy +import pickle from email.mime.image import MIMEImage from unittest import skipIf @@ -24,7 +26,7 @@ from anymail.utils import ( parse_address_list, parse_single_address, EmailAddress, Attachment, is_lazy, force_non_lazy, force_non_lazy_dict, force_non_lazy_list, - update_deep, + update_deep, UNSET, get_request_uri, get_request_basic_auth, parse_rfc2822date, querydict_getfirst, CaseInsensitiveCasePreservingDict) @@ -441,3 +443,30 @@ class CaseInsensitiveCasePreservingDictTests(SimpleTestCase): # The base CaseInsensitiveDict functionality is well-tested in Requests, # so we don't repeat it here. + + +class UnsetValueTests(SimpleTestCase): + """Tests for the UNSET sentinel value""" + + def test_not_other_values(self): + self.assertIsNot(UNSET, None) + self.assertIsNot(UNSET, False) + self.assertNotEqual(UNSET, 0) + self.assertNotEqual(UNSET, "") + + def test_unset_survives_pickle(self): + # Required for using AnymailMessage with django-mailer + pickled = pickle.dumps(UNSET) + self.assertIs(pickle.loads(pickled), UNSET) + + def test_unset_survives_copy(self): + self.assertIs(copy.copy(UNSET), UNSET) + self.assertIs(copy.deepcopy(UNSET), UNSET) + + def test_unset_has_useful_repr(self): + # (something better than '') + self.assertIn("UNSET", repr(UNSET)) + + def test_equality(self): + # `is UNSET` is preferred to `== UNSET`, but both should work + self.assertEqual(UNSET, UNSET)