Skip to content

Commit e548f6c

Browse files
committed
SendGrid: Remove obsolete display name workaround
1 parent 85decd8 commit e548f6c

File tree

3 files changed

+24
-59
lines changed

3 files changed

+24
-59
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ Fixes
5656
* **Postmark:** Fix an error in inbound handling with long email address display
5757
names that include non-ASCII characters.
5858

59+
Other
60+
~~~~~
61+
62+
* **SendGrid:** Remove Anymail's workaround for an earlier SendGrid API bug with
63+
punctuation in address display names. SendGrid has fixed the bug.
64+
5965

6066
v12.0
6167
-----

anymail/backends/sendgrid.py

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import uuid
22
import warnings
3-
from email.utils import quote as rfc822_quote
43

54
from requests.structures import CaseInsensitiveDict
65

@@ -45,15 +44,6 @@ def __init__(self, **kwargs):
4544
"merge_field_format", esp_name=esp_name, kwargs=kwargs, default=None
4645
)
4746

48-
# Undocumented setting to disable workaround for SendGrid display-name quoting
49-
# bug (see below). If/when SendGrid fixes their API, recipient names will end up
50-
# with extra double quotes until Anymail is updated to remove the workaround.
51-
# In the meantime, you can disable it by adding
52-
# `"SENDGRID_WORKAROUND_NAME_QUOTE_BUG": False` to your `ANYMAIL` settings.
53-
self.workaround_name_quote_bug = get_anymail_setting(
54-
"workaround_name_quote_bug", esp_name=esp_name, kwargs=kwargs, default=True
55-
)
56-
5747
# This is SendGrid's newer Web API v3
5848
api_url = get_anymail_setting(
5949
"api_url",
@@ -85,7 +75,6 @@ class SendGridPayload(RequestsPayload):
8575
def __init__(self, message, defaults, backend, *args, **kwargs):
8676
self.all_recipients = [] # used for backend.parse_recipient_status
8777
self.generate_message_id = backend.generate_message_id
88-
self.workaround_name_quote_bug = backend.workaround_name_quote_bug
8978
self.use_dynamic_template = False # how to represent merge_data
9079
self.message_ids = {} # recipient -> generated message_id mapping
9180
self.merge_field_format = backend.merge_field_format
@@ -232,18 +221,11 @@ def build_merge_headers(self):
232221
#
233222

234223
@staticmethod
235-
def email_object(email, workaround_name_quote_bug=False):
224+
def email_object(email):
236225
"""Converts EmailAddress to SendGrid API {email, name} dict"""
237226
obj = {"email": email.addr_spec}
238227
if email.display_name:
239-
# Work around SendGrid API bug: v3 fails to properly quote display-names
240-
# containing commas or semicolons in personalizations (but not in from_email
241-
# or reply_to). See https://github.com/sendgrid/sendgrid-python/issues/291.
242-
# We can work around the problem by quoting the name for SendGrid.
243-
if workaround_name_quote_bug:
244-
obj["name"] = '"%s"' % rfc822_quote(email.display_name)
245-
else:
246-
obj["name"] = email.display_name
228+
obj["name"] = email.display_name
247229
return obj
248230

249231
def set_from_email(self, email):
@@ -252,11 +234,10 @@ def set_from_email(self, email):
252234
def set_recipients(self, recipient_type, emails):
253235
assert recipient_type in ["to", "cc", "bcc"]
254236
if emails:
255-
workaround_name_quote_bug = self.workaround_name_quote_bug
256237
# Normally, exactly one "personalizations" entry for all recipients
257238
# (Exception: with merge_data; will be burst apart later.)
258239
self.data["personalizations"][0][recipient_type] = [
259-
self.email_object(email, workaround_name_quote_bug) for email in emails
240+
self.email_object(email) for email in emails
260241
]
261242
self.all_recipients += emails # used for backend.parse_recipient_status
262243

tests/test_sendgrid_backend.py

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -118,27 +118,25 @@ def test_name_addr(self):
118118
)
119119

120120
# single message (single "personalization") sent to all those recipients
121-
# (note workaround for SendGrid v3 API bug quoting display-name
122-
# in personalizations)
123121
self.assertEqual(len(data["personalizations"]), 1)
124122
self.assertEqual(
125123
data["personalizations"][0]["to"],
126124
[
127-
{"name": '"Recipient #1"', "email": "to1@example.com"},
125+
{"name": "Recipient #1", "email": "to1@example.com"},
128126
{"email": "to2@example.com"},
129127
],
130128
)
131129
self.assertEqual(
132130
data["personalizations"][0]["cc"],
133131
[
134-
{"name": '"Carbon Copy"', "email": "cc1@example.com"},
132+
{"name": "Carbon Copy", "email": "cc1@example.com"},
135133
{"email": "cc2@example.com"},
136134
],
137135
)
138136
self.assertEqual(
139137
data["personalizations"][0]["bcc"],
140138
[
141-
{"name": '"Blind Copy"', "email": "bcc1@example.com"},
139+
{"name": "Blind Copy", "email": "bcc1@example.com"},
142140
{"email": "bcc2@example.com"},
143141
],
144142
)
@@ -166,15 +164,15 @@ def test_email_message(self):
166164
{
167165
"to": [
168166
{"email": "to1@example.com"},
169-
{"email": "to2@example.com", "name": '"Also To"'},
167+
{"email": "to2@example.com", "name": "Also To"},
170168
],
171169
"cc": [
172170
{"email": "cc1@example.com"},
173-
{"email": "cc2@example.com", "name": '"Also CC"'},
171+
{"email": "cc2@example.com", "name": "Also CC"},
174172
],
175173
"bcc": [
176174
{"email": "bcc1@example.com"},
177-
{"email": "bcc2@example.com", "name": '"Also BCC"'},
175+
{"email": "bcc2@example.com", "name": "Also BCC"},
178176
],
179177
# make sure custom Message-ID also added to custom_args
180178
"custom_args": {"anymail_id": "mocked-uuid-1"},
@@ -610,7 +608,7 @@ def test_merge_data(self):
610608
},
611609
},
612610
{
613-
"to": [{"email": "bob@example.com", "name": '"Bob"'}],
611+
"to": [{"email": "bob@example.com", "name": "Bob"}],
614612
"cc": [{"email": "cc@example.com"}],
615613
"custom_args": {"anymail_id": "mocked-uuid-2"},
616614
"dynamic_template_data": {
@@ -733,7 +731,7 @@ def test_legacy_merge_data(self):
733731
},
734732
},
735733
{
736-
"to": [{"email": "bob@example.com", "name": '"Bob"'}],
734+
"to": [{"email": "bob@example.com", "name": "Bob"}],
737735
"cc": [{"email": "cc@example.com"}],
738736
"custom_args": {"anymail_id": "mocked-uuid-2"},
739737
"substitutions": {
@@ -779,7 +777,7 @@ def test_legacy_merge_field_format_setting(self):
779777
},
780778
},
781779
{
782-
"to": [{"email": "bob@example.com", "name": '"Bob"'}],
780+
"to": [{"email": "bob@example.com", "name": "Bob"}],
783781
"custom_args": {"anymail_id": "mocked-uuid-2"},
784782
"substitutions": {":name": "Bob", ":site": "ExampleCo"},
785783
},
@@ -811,7 +809,7 @@ def test_legacy_merge_field_format_esp_extra(self):
811809
},
812810
},
813811
{
814-
"to": [{"email": "bob@example.com", "name": '"Bob"'}],
812+
"to": [{"email": "bob@example.com", "name": "Bob"}],
815813
"custom_args": {"anymail_id": "mocked-uuid-2"},
816814
"substitutions": {"*|name|*": "Bob", "*|site|*": "ExampleCo"},
817815
},
@@ -850,7 +848,7 @@ def test_merge_metadata(self):
850848
"custom_args": {"anymail_id": "mocked-uuid-1", "order_id": "123"},
851849
},
852850
{
853-
"to": [{"email": "bob@example.com", "name": '"Bob"'}],
851+
"to": [{"email": "bob@example.com", "name": "Bob"}],
854852
"custom_args": {
855853
"anymail_id": "mocked-uuid-2",
856854
"order_id": "678",
@@ -881,7 +879,7 @@ def test_metadata_with_merge_metadata(self):
881879
"custom_args": {"anymail_id": "mocked-uuid-1", "order_id": "123"},
882880
},
883881
{
884-
"to": [{"email": "bob@example.com", "name": '"Bob"'}],
882+
"to": [{"email": "bob@example.com", "name": "Bob"}],
885883
"custom_args": {
886884
"anymail_id": "mocked-uuid-2",
887885
"order_id": "678",
@@ -932,7 +930,7 @@ def test_merge_metadata_with_merge_data(self):
932930
"custom_args": {"anymail_id": "mocked-uuid-1", "order_id": "123"},
933931
},
934932
{
935-
"to": [{"email": "bob@example.com", "name": '"Bob"'}],
933+
"to": [{"email": "bob@example.com", "name": "Bob"}],
936934
"cc": [{"email": "cc@example.com"}],
937935
"dynamic_template_data": {
938936
"name": "Bob",
@@ -994,7 +992,7 @@ def test_merge_metadata_with_legacy_template(self):
994992
},
995993
},
996994
{
997-
"to": [{"email": "bob@example.com", "name": '"Bob"'}],
995+
"to": [{"email": "bob@example.com", "name": "Bob"}],
998996
"cc": [{"email": "cc@example.com"}],
999997
"custom_args": {
1000998
"anymail_id": "mocked-uuid-2",
@@ -1130,7 +1128,7 @@ def test_esp_extra_pesonalizations(self):
11301128
data["personalizations"],
11311129
[
11321130
{
1133-
"to": [{"email": "first@example.com", "name": '"First recipient"'}],
1131+
"to": [{"email": "first@example.com", "name": "First recipient"}],
11341132
"custom_args": {"anymail_id": "mocked-uuid-1"},
11351133
"future_feature": "works",
11361134
},
@@ -1248,26 +1246,6 @@ def test_json_serialization_errors(self):
12481246
# original message:
12491247
self.assertRegex(str(err), r"Decimal.*is not JSON serializable")
12501248

1251-
@override_settings(ANYMAIL_SENDGRID_WORKAROUND_NAME_QUOTE_BUG=False)
1252-
def test_undocumented_workaround_name_quote_bug_setting(self):
1253-
mail.send_mail(
1254-
"Subject",
1255-
"Body",
1256-
'"Sender, Inc." <from@example.com',
1257-
['"Recipient, Ltd." <to@example.com>'],
1258-
)
1259-
data = self.get_api_call_json()
1260-
self.assertEqual(
1261-
data["personalizations"][0]["to"][0],
1262-
{
1263-
"email": "to@example.com",
1264-
"name": "Recipient, Ltd.", # no extra quotes on name
1265-
},
1266-
)
1267-
self.assertEqual(
1268-
data["from"], {"email": "from@example.com", "name": "Sender, Inc."}
1269-
)
1270-
12711249

12721250
@tag("sendgrid")
12731251
class SendGridBackendRecipientsRefusedTests(SendGridBackendMockAPITestCase):

0 commit comments

Comments
 (0)