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.)
This commit is contained in:
medmunds
2017-10-27 17:53:13 -07:00
parent bb68f3dd6d
commit 9acf6501b5
10 changed files with 40 additions and 56 deletions

View File

@@ -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

View File

@@ -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"]

View File

@@ -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):

View File

@@ -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

View File

@@ -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):

View File

@@ -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):

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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$")