mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 03:41:05 -05:00
Internal: add CaseInsensitiveCasePreservingDict
Like CaseInsensitiveDict (which we borrow from Requests), but preserves case of the first key set rather than the last.
This commit is contained in:
@@ -12,6 +12,7 @@ from django.core.mail.message import sanitize_address, DEFAULT_ATTACHMENT_MIME_T
|
|||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.functional import Promise
|
from django.utils.functional import Promise
|
||||||
from django.utils.timezone import utc, get_fixed_timezone
|
from django.utils.timezone import utc, get_fixed_timezone
|
||||||
|
from requests.structures import CaseInsensitiveDict
|
||||||
from six.moves.urllib.parse import urlsplit, urlunsplit
|
from six.moves.urllib.parse import urlsplit, urlunsplit
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -571,3 +572,34 @@ def parse_rfc2822date(s):
|
|||||||
except (IndexError, TypeError, ValueError):
|
except (IndexError, TypeError, ValueError):
|
||||||
# despite the docs, parsedate_to_datetime often dies on unparseable input
|
# despite the docs, parsedate_to_datetime often dies on unparseable input
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class CaseInsensitiveCasePreservingDict(CaseInsensitiveDict):
|
||||||
|
"""A dict with case-insensitive keys, which preserves the *first* key set.
|
||||||
|
|
||||||
|
>>> cicpd = CaseInsensitiveCasePreservingDict()
|
||||||
|
>>> cicpd["Accept"] = "application/text+xml"
|
||||||
|
>>> cicpd["accEPT"] = "application/json"
|
||||||
|
>>> cicpd["accept"]
|
||||||
|
"application/json"
|
||||||
|
>>> cicpd.keys()
|
||||||
|
["Accept"]
|
||||||
|
|
||||||
|
Compare to CaseInsensitiveDict, which preserves *last* key set:
|
||||||
|
>>> cid = CaseInsensitiveCasePreservingDict()
|
||||||
|
>>> cid["Accept"] = "application/text+xml"
|
||||||
|
>>> cid["accEPT"] = "application/json"
|
||||||
|
>>> cid.keys()
|
||||||
|
["accEPT"]
|
||||||
|
"""
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
_k = key.lower()
|
||||||
|
try:
|
||||||
|
# retrieve earlier matching key, if any
|
||||||
|
key, _ = self._store[_k]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
self._store[_k] = (key, value)
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return self.__class__(self._store.values())
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ from anymail.utils import (
|
|||||||
Attachment,
|
Attachment,
|
||||||
is_lazy, force_non_lazy, force_non_lazy_dict, force_non_lazy_list,
|
is_lazy, force_non_lazy, force_non_lazy_dict, force_non_lazy_list,
|
||||||
update_deep,
|
update_deep,
|
||||||
get_request_uri, get_request_basic_auth, parse_rfc2822date, querydict_getfirst)
|
get_request_uri, get_request_basic_auth, parse_rfc2822date, querydict_getfirst,
|
||||||
|
CaseInsensitiveCasePreservingDict)
|
||||||
|
|
||||||
|
|
||||||
class ParseAddressListTests(SimpleTestCase):
|
class ParseAddressListTests(SimpleTestCase):
|
||||||
@@ -415,3 +416,28 @@ class LazyErrorTests(SimpleTestCase):
|
|||||||
lazy = _LazyError(ValueError("lazy failure")) # creating doesn't cause error
|
lazy = _LazyError(ValueError("lazy failure")) # creating doesn't cause error
|
||||||
with self.assertRaisesMessage(ValueError, "lazy failure"):
|
with self.assertRaisesMessage(ValueError, "lazy failure"):
|
||||||
self.unused = lazy() # call *does* cause error
|
self.unused = lazy() # call *does* cause error
|
||||||
|
|
||||||
|
|
||||||
|
class CaseInsensitiveCasePreservingDictTests(SimpleTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.dict = CaseInsensitiveCasePreservingDict()
|
||||||
|
self.dict["Accept"] = "application/text+xml"
|
||||||
|
self.dict["accEPT"] = "application/json"
|
||||||
|
|
||||||
|
def test_preserves_first_key(self):
|
||||||
|
self.assertEqual(list(self.dict.keys()), ["Accept"])
|
||||||
|
|
||||||
|
def test_copy(self):
|
||||||
|
copy = self.dict.copy()
|
||||||
|
self.assertIsNot(copy, self.dict)
|
||||||
|
self.assertEqual(copy, self.dict)
|
||||||
|
# Here's why the superclass CaseInsensitiveDict.copy is insufficient:
|
||||||
|
self.assertIsInstance(copy, CaseInsensitiveCasePreservingDict)
|
||||||
|
|
||||||
|
def test_get_item(self):
|
||||||
|
self.assertEqual(self.dict["accept"], "application/json")
|
||||||
|
self.assertEqual(self.dict["Accept"], "application/json")
|
||||||
|
self.assertEqual(self.dict["accEPT"], "application/json")
|
||||||
|
|
||||||
|
# The base CaseInsensitiveDict functionality is well-tested in Requests,
|
||||||
|
# so we don't repeat it here.
|
||||||
|
|||||||
Reference in New Issue
Block a user