From 9acf6501b583fa490efb6af38558663e257c6f4a Mon Sep 17 00:00:00 2001 From: medmunds Date: Fri, 27 Oct 2017 17:53:13 -0700 Subject: [PATCH] Utils: Finish ParsedEmail --> EmailAddress conversion Within an EmailAddress (previously ParsedEmail object), properties now match Python 3.6 email.headerregistry.Address naming: * .email --> .addr_spec * .name --> .display_name * .localpart --> .username (Completes work started in 386668908423d1d4eade90cf7a21a546a1e96514; this updates remaining uses of old names and removes them.) --- anymail/backends/mailgun.py | 4 ++-- anymail/backends/mailjet.py | 20 ++++++++++---------- anymail/backends/mandrill.py | 8 ++++---- anymail/backends/postmark.py | 4 ++-- anymail/backends/sendgrid.py | 12 ++++++------ anymail/backends/sendgrid_v2.py | 14 +++++++------- anymail/backends/sparkpost.py | 10 +++++----- anymail/backends/test.py | 6 +++--- anymail/utils.py | 16 ---------------- tests/test_send_signals.py | 2 +- 10 files changed, 40 insertions(+), 56 deletions(-) diff --git a/anymail/backends/mailgun.py b/anymail/backends/mailgun.py index 6ba75da..ee1328d 100644 --- a/anymail/backends/mailgun.py +++ b/anymail/backends/mailgun.py @@ -53,7 +53,7 @@ class EmailBackend(AnymailRequestsBackend): backend=self) # Simulate a per-recipient status of "queued": status = AnymailRecipientStatus(message_id=message_id, status="queued") - return {recipient.email: status for recipient in payload.all_recipients} + return {recipient.addr_spec: status for recipient in payload.all_recipients} class MailgunPayload(RequestsPayload): @@ -127,7 +127,7 @@ class MailgunPayload(RequestsPayload): self.data[recipient_type] = [email.address for email in emails] self.all_recipients += emails # used for backend.parse_recipient_status if recipient_type == 'to': - self.to_emails = [email.email for email in emails] # used for populate_recipient_variables + self.to_emails = [email.addr_spec for email in emails] # used for populate_recipient_variables def set_subject(self, subject): self.data["subject"] = subject diff --git a/anymail/backends/mailjet.py b/anymail/backends/mailjet.py index f8652ef..6dd8003 100644 --- a/anymail/backends/mailjet.py +++ b/anymail/backends/mailjet.py @@ -65,8 +65,8 @@ class EmailBackend(AnymailRequestsBackend): # (Mailjet only communicates "Sent") for recipients in payload.recipients.values(): for email in recipients: - if email.email not in recipient_status: - recipient_status[email.email] = AnymailRecipientStatus(message_id=None, status='unknown') + if email.addr_spec not in recipient_status: + recipient_status[email.addr_spec] = AnymailRecipientStatus(message_id=None, status='unknown') return recipient_status @@ -144,9 +144,9 @@ class MailjetPayload(RequestsPayload): merge_data = self.merge_data or {} for email in self.recipients["to"]: recipient = { - "Email": email.email, - "Name": email.name, - "Vars": merge_data.get(email.email) + "Email": email.addr_spec, + "Name": email.display_name, + "Vars": merge_data.get(email.addr_spec) } # Strip out empty Name and Vars recipient = {k: v for k, v in recipient.items() if v} @@ -163,9 +163,9 @@ class MailjetPayload(RequestsPayload): # Workaround Mailjet 3.0 bug parsing display-name with commas # (see test_comma_in_display_name in test_mailjet_backend for details) formatted_emails = [ - email.address if "," not in email.name + email.address if "," not in email.display_name # else name has a comma, so force it into MIME encoded-word utf-8 syntax: - else EmailAddress(email.name.encode('utf-8'), email.email).formataddr('utf-8') + else EmailAddress(email.display_name.encode('utf-8'), email.addr_spec).formataddr('utf-8') for email in emails ] self.data[recipient_type.capitalize()] = ", ".join(formatted_emails) @@ -175,9 +175,9 @@ class MailjetPayload(RequestsPayload): } def set_from_email(self, email): - self.data["FromEmail"] = email.email - if email.name: - self.data["FromName"] = email.name + self.data["FromEmail"] = email.addr_spec + if email.display_name: + self.data["FromName"] = email.display_name def set_recipients(self, recipient_type, emails): assert recipient_type in ["to", "cc", "bcc"] diff --git a/anymail/backends/mandrill.py b/anymail/backends/mandrill.py index 758e7c4..efe3197 100644 --- a/anymail/backends/mandrill.py +++ b/anymail/backends/mandrill.py @@ -95,14 +95,14 @@ class MandrillPayload(RequestsPayload): if getattr(self.message, "use_template_from", False): self.deprecation_warning('message.use_template_from', 'message.from_email = None') else: - self.data["message"]["from_email"] = email.email - if email.name: - self.data["message"]["from_name"] = email.name + self.data["message"]["from_email"] = email.addr_spec + if email.display_name: + self.data["message"]["from_name"] = email.display_name def add_recipient(self, recipient_type, email): assert recipient_type in ["to", "cc", "bcc"] to_list = self.data["message"].setdefault("to", []) - to_list.append({"email": email.email, "name": email.name, "type": recipient_type}) + to_list.append({"email": email.addr_spec, "name": email.display_name, "type": recipient_type}) def set_subject(self, subject): if getattr(self.message, "use_template_subject", False): diff --git a/anymail/backends/postmark.py b/anymail/backends/postmark.py index a144e9a..3a2e840 100644 --- a/anymail/backends/postmark.py +++ b/anymail/backends/postmark.py @@ -69,9 +69,9 @@ class EmailBackend(AnymailRequestsBackend): backend=self) return { - recipient.email: AnymailRecipientStatus( + recipient.addr_spec: AnymailRecipientStatus( message_id=message_id, - status=('rejected' if recipient.email.lower() in rejected_emails + status=('rejected' if recipient.addr_spec.lower() in rejected_emails else default_status) ) for recipient in payload.all_recipients diff --git a/anymail/backends/sendgrid.py b/anymail/backends/sendgrid.py index 1e702d5..89ddff2 100644 --- a/anymail/backends/sendgrid.py +++ b/anymail/backends/sendgrid.py @@ -64,7 +64,7 @@ class EmailBackend(AnymailRequestsBackend): # SendGrid v3 doesn't provide any information in the response for a successful send, # so simulate a per-recipient status of "queued": status = AnymailRecipientStatus(message_id=payload.message_id, status="queued") - return {recipient.email: status for recipient in payload.all_recipients} + return {recipient.addr_spec: status for recipient in payload.all_recipients} class SendGridPayload(RequestsPayload): @@ -199,17 +199,17 @@ class SendGridPayload(RequestsPayload): @staticmethod def email_object(email, workaround_name_quote_bug=False): - """Converts ParsedEmail to SendGrid API {email, name} dict""" - obj = {"email": email.email} - if email.name: + """Converts EmailAddress to SendGrid API {email, name} dict""" + obj = {"email": email.addr_spec} + if email.display_name: # Work around SendGrid API bug: v3 fails to properly quote display-names # containing commas or semicolons in personalizations (but not in from_email # or reply_to). See https://github.com/sendgrid/sendgrid-python/issues/291. # We can work around the problem by quoting the name for SendGrid. if workaround_name_quote_bug: - obj["name"] = '"%s"' % rfc822_quote(email.name) + obj["name"] = '"%s"' % rfc822_quote(email.display_name) else: - obj["name"] = email.name + obj["name"] = email.display_name return obj def set_from_email(self, email): diff --git a/anymail/backends/sendgrid_v2.py b/anymail/backends/sendgrid_v2.py index 889b763..41138b2 100644 --- a/anymail/backends/sendgrid_v2.py +++ b/anymail/backends/sendgrid_v2.py @@ -63,7 +63,7 @@ class EmailBackend(AnymailRequestsBackend): backend=self) # Simulate a per-recipient status of "queued": status = AnymailRecipientStatus(message_id=payload.message_id, status="queued") - return {recipient.email: status for recipient in payload.all_recipients} + return {recipient.addr_spec: status for recipient in payload.all_recipients} class SendGridPayload(RequestsPayload): @@ -166,7 +166,7 @@ class SendGridPayload(RequestsPayload): all_fields = set() for recipient_data in self.merge_data.values(): all_fields = all_fields.union(recipient_data.keys()) - recipients = [email.email for email in self.to_list] + recipients = [email.addr_spec for email in self.to_list] if self.merge_field_format is None and all(field.isalnum() for field in all_fields): warnings.warn( @@ -203,9 +203,9 @@ class SendGridPayload(RequestsPayload): self.data['headers'] = CaseInsensitiveDict() # headers keys are case-insensitive def set_from_email(self, email): - self.data["from"] = email.email - if email.name: - self.data["fromname"] = email.name + self.data["from"] = email.addr_spec + if email.display_name: + self.data["fromname"] = email.display_name def set_to(self, emails): self.to_list = emails # track for later use by build_merge_data @@ -214,9 +214,9 @@ class SendGridPayload(RequestsPayload): def set_recipients(self, recipient_type, emails): assert recipient_type in ["to", "cc", "bcc"] if emails: - self.data[recipient_type] = [email.email for email in emails] + self.data[recipient_type] = [email.addr_spec for email in emails] empty_name = " " # SendGrid API balks on complete empty name fields - self.data[recipient_type + "name"] = [email.name or empty_name for email in emails] + self.data[recipient_type + "name"] = [email.display_name or empty_name for email in emails] self.all_recipients += emails # used for backend.parse_recipient_status def set_subject(self, subject): diff --git a/anymail/backends/sparkpost.py b/anymail/backends/sparkpost.py index 20e652e..622925f 100644 --- a/anymail/backends/sparkpost.py +++ b/anymail/backends/sparkpost.py @@ -76,7 +76,7 @@ class EmailBackend(AnymailBaseBackend): else: # mixed results, or wrong total status = 'unknown' recipient_status = AnymailRecipientStatus(message_id=transmission_id, status=status) - return {recipient.email: recipient_status for recipient in payload.all_recipients} + return {recipient.addr_spec: recipient_status for recipient in payload.all_recipients} class SparkPostPayload(BasePayload): @@ -92,11 +92,11 @@ class SparkPostPayload(BasePayload): if len(self.merge_data) > 0: # Build JSON recipient structures for email in self.to_emails: - rcpt = {'address': {'email': email.email}} - if email.name: - rcpt['address']['name'] = email.name + rcpt = {'address': {'email': email.addr_spec}} + if email.display_name: + rcpt['address']['name'] = email.display_name try: - rcpt['substitution_data'] = self.merge_data[email.email] + rcpt['substitution_data'] = self.merge_data[email.addr_spec] except KeyError: pass # no merge_data or none for this recipient recipients.append(rcpt) diff --git a/anymail/backends/test.py b/anymail/backends/test.py index cc7c407..41f900b 100644 --- a/anymail/backends/test.py +++ b/anymail/backends/test.py @@ -68,15 +68,15 @@ class TestPayload(BasePayload): def set_to(self, emails): self.params['to'] = emails - self.recipient_emails += [email.email for email in emails] + self.recipient_emails += [email.addr_spec for email in emails] def set_cc(self, emails): self.params['cc'] = emails - self.recipient_emails += [email.email for email in emails] + self.recipient_emails += [email.addr_spec for email in emails] def set_bcc(self, emails): self.params['bcc'] = emails - self.recipient_emails += [email.email for email in emails] + self.recipient_emails += [email.addr_spec for email in emails] def set_subject(self, subject): self.params['subject'] = subject diff --git a/anymail/utils.py b/anymail/utils.py index cabb251..69ead1b 100644 --- a/anymail/utils.py +++ b/anymail/utils.py @@ -232,22 +232,6 @@ class EmailAddress(object): def __str__(self): return self.address - # Deprecated property names from old ParsedEmail (don't use in new code!) - @property - def name(self): - return self.display_name - - @property - def email(self): - return self.addr_spec - - @property - def localpart(self): - return self.username - - -ParsedEmail = EmailAddress # deprecated class name (don't use!) - class Attachment(object): """A normalized EmailMessage.attachments item with additional functionality diff --git a/tests/test_send_signals.py b/tests/test_send_signals.py index de42177..b416c14 100644 --- a/tests/test_send_signals.py +++ b/tests/test_send_signals.py @@ -38,7 +38,7 @@ class TestPreSendSignal(TestBackendTestCase): self.message.to = ['legit@example.com', 'bad@example.com'] self.message.send() params = self.get_send_params() - self.assertEqual([email.email for email in params['to']], # params['to'] is ParsedEmail list + self.assertEqual([email.addr_spec for email in params['to']], # params['to'] is EmailAddress list ['legit@example.com']) self.assertRegex(params['text_body'], r"If you have received this message in error, ignore it$")