Rename EmailBackends for Django consistency

* **Future breaking change:**
  Rename all Anymail backends to just `EmailBackend`,
  matching Django's naming convention.
  (E.g., switch to "anymail.backends.mailgun.EmailBackend"
  rather than "anymail.backends.mailgun.MailgunBackend".)

  The old names still work, but will issue a DeprecationWarning
  and will be removed in some future release.

  (Apologies for this change; the old naming convention was
  a holdover from Djrill, and I wanted consistency with
  other Django EmailBackends before hitting 1.0.)

Fixes #49.
This commit is contained in:
medmunds
2017-01-20 15:47:37 -08:00
parent bff01b440a
commit 79288603fb
29 changed files with 190 additions and 71 deletions

View File

@@ -101,7 +101,7 @@ or SparkPost or any other supported ESP where you see "mailgun":
"MAILGUN_API_KEY": "<your Mailgun key>",
"MAILGUN_SENDER_DOMAIN": 'mg.example.com', # your Mailgun domain, if needed
}
EMAIL_BACKEND = "anymail.backends.mailgun.MailgunBackend" # or sendgrid.SendGridBackend, or...
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" # or sendgrid.EmailBackend, or...
DEFAULT_FROM_EMAIL = "you@example.com" # if you don't already have this in settings

View File

@@ -190,9 +190,12 @@ class AnymailBaseBackend(BaseEmailBackend):
"""
Read-only name of the ESP for this backend.
(E.g., MailgunBackend will return "Mailgun")
Concrete backends must override with class attr. E.g.:
esp_name = "Postmark"
esp_name = "SendGrid" # (use ESP's preferred capitalization)
"""
return self.__class__.__name__.replace("Backend", "")
raise NotImplementedError("%s.%s must declare esp_name class attr" %
(self.__class__.__module__, self.__class__.__name__))
class BasePayload(object):

View File

@@ -1,17 +1,20 @@
import warnings
from datetime import datetime
from ..exceptions import AnymailRequestsAPIError, AnymailError
from ..exceptions import AnymailRequestsAPIError, AnymailError, AnymailDeprecationWarning
from ..message import AnymailRecipientStatus
from ..utils import get_anymail_setting, rfc2822date
from .base_requests import AnymailRequestsBackend, RequestsPayload
class MailgunBackend(AnymailRequestsBackend):
class EmailBackend(AnymailRequestsBackend):
"""
Mailgun API Email Backend
"""
esp_name = "Mailgun"
def __init__(self, **kwargs):
"""Init options from Django settings"""
esp_name = self.esp_name
@@ -22,7 +25,7 @@ class MailgunBackend(AnymailRequestsBackend):
default="https://api.mailgun.net/v3")
if not api_url.endswith("/"):
api_url += "/"
super(MailgunBackend, self).__init__(api_url, **kwargs)
super(EmailBackend, self).__init__(api_url, **kwargs)
def build_message_payload(self, message, defaults):
return MailgunPayload(message, defaults, self)
@@ -52,6 +55,15 @@ class MailgunBackend(AnymailRequestsBackend):
return {recipient.email: status for recipient in payload.all_recipients}
# Pre-v0.8 naming (deprecated)
class MailgunBackend(EmailBackend):
def __init__(self, **kwargs):
warnings.warn(AnymailDeprecationWarning(
"Please update your EMAIL_BACKEND setting to "
"'anymail.backends.mailgun.EmailBackend'"))
super(MailgunBackend, self).__init__(**kwargs)
class MailgunPayload(RequestsPayload):
def __init__(self, message, defaults, backend, *args, **kwargs):

View File

@@ -1,18 +1,20 @@
import warnings
from datetime import datetime
from ..exceptions import AnymailRequestsAPIError, AnymailWarning
from ..exceptions import AnymailRequestsAPIError, AnymailWarning, AnymailDeprecationWarning
from ..message import AnymailRecipientStatus, ANYMAIL_STATUSES
from ..utils import last, combine, get_anymail_setting
from .base_requests import AnymailRequestsBackend, RequestsPayload
class MandrillBackend(AnymailRequestsBackend):
class EmailBackend(AnymailRequestsBackend):
"""
Mandrill API Email Backend
"""
esp_name = "Mandrill"
def __init__(self, **kwargs):
"""Init options from Django settings"""
esp_name = self.esp_name
@@ -21,7 +23,7 @@ class MandrillBackend(AnymailRequestsBackend):
default="https://mandrillapp.com/api/1.0")
if not api_url.endswith("/"):
api_url += "/"
super(MandrillBackend, self).__init__(api_url, **kwargs)
super(EmailBackend, self).__init__(api_url, **kwargs)
def build_message_payload(self, message, defaults):
return MandrillPayload(message, defaults, self)
@@ -44,6 +46,15 @@ class MandrillBackend(AnymailRequestsBackend):
return recipient_status
# Pre-v0.8 naming (deprecated)
class MandrillBackend(EmailBackend):
def __init__(self, **kwargs):
warnings.warn(AnymailDeprecationWarning(
"Please update your EMAIL_BACKEND setting to "
"'anymail.backends.mandrill.EmailBackend'"))
super(MandrillBackend, self).__init__(**kwargs)
class DjrillDeprecationWarning(AnymailWarning, DeprecationWarning):
"""Warning for features carried over from Djrill that will be removed soon"""

View File

@@ -1,19 +1,22 @@
import re
import warnings
from requests.structures import CaseInsensitiveDict
from ..exceptions import AnymailRequestsAPIError
from ..exceptions import AnymailRequestsAPIError, AnymailDeprecationWarning
from ..message import AnymailRecipientStatus
from ..utils import get_anymail_setting
from .base_requests import AnymailRequestsBackend, RequestsPayload
class PostmarkBackend(AnymailRequestsBackend):
class EmailBackend(AnymailRequestsBackend):
"""
Postmark API Email Backend
"""
esp_name = "Postmark"
def __init__(self, **kwargs):
"""Init options from Django settings"""
esp_name = self.esp_name
@@ -22,7 +25,7 @@ class PostmarkBackend(AnymailRequestsBackend):
default="https://api.postmarkapp.com/")
if not api_url.endswith("/"):
api_url += "/"
super(PostmarkBackend, self).__init__(api_url, **kwargs)
super(EmailBackend, self).__init__(api_url, **kwargs)
def build_message_payload(self, message, defaults):
return PostmarkPayload(message, defaults, self)
@@ -30,7 +33,7 @@ class PostmarkBackend(AnymailRequestsBackend):
def raise_for_status(self, response, payload, message):
# We need to handle 422 responses in parse_recipient_status
if response.status_code != 422:
super(PostmarkBackend, self).raise_for_status(response, payload, message)
super(EmailBackend, self).raise_for_status(response, payload, message)
def parse_recipient_status(self, response, payload, message):
parsed_response = self.deserialize_json_response(response, payload, message)
@@ -89,6 +92,15 @@ class PostmarkBackend(AnymailRequestsBackend):
return []
# Pre-v0.8 naming (deprecated)
class PostmarkBackend(EmailBackend):
def __init__(self, **kwargs):
warnings.warn(AnymailDeprecationWarning(
"Please update your EMAIL_BACKEND setting to "
"'anymail.backends.postmark.EmailBackend'"))
super(PostmarkBackend, self).__init__(**kwargs)
class PostmarkPayload(RequestsPayload):
def __init__(self, message, defaults, backend, *args, **kwargs):

View File

@@ -5,16 +5,18 @@ from django.core.mail import make_msgid
from requests.structures import CaseInsensitiveDict
from .base_requests import AnymailRequestsBackend, RequestsPayload
from ..exceptions import AnymailConfigurationError, AnymailRequestsAPIError, AnymailWarning
from ..exceptions import AnymailConfigurationError, AnymailRequestsAPIError, AnymailWarning, AnymailDeprecationWarning
from ..message import AnymailRecipientStatus
from ..utils import get_anymail_setting, timestamp, update_deep
class SendGridBackend(AnymailRequestsBackend):
class EmailBackend(AnymailRequestsBackend):
"""
SendGrid v3 API Email Backend
"""
esp_name = "SendGrid"
def __init__(self, **kwargs):
"""Init options from Django settings"""
esp_name = self.esp_name
@@ -46,7 +48,7 @@ class SendGridBackend(AnymailRequestsBackend):
default="https://api.sendgrid.com/v3/")
if not api_url.endswith("/"):
api_url += "/"
super(SendGridBackend, self).__init__(api_url, **kwargs)
super(EmailBackend, self).__init__(api_url, **kwargs)
def build_message_payload(self, message, defaults):
return SendGridPayload(message, defaults, self)
@@ -64,6 +66,15 @@ class SendGridBackend(AnymailRequestsBackend):
return {recipient.email: status for recipient in payload.all_recipients}
# Pre-v0.8 naming (deprecated)
class SendGridBackend(EmailBackend):
def __init__(self, **kwargs):
warnings.warn(AnymailDeprecationWarning(
"Please update your EMAIL_BACKEND setting to "
"'anymail.backends.sendgrid.EmailBackend'"))
super(SendGridBackend, self).__init__(**kwargs)
class SendGridPayload(RequestsPayload):
def __init__(self, message, defaults, backend, *args, **kwargs):

View File

@@ -260,7 +260,7 @@ class SendGridPayload(RequestsPayload):
if files_field in self.files:
# It's possible SendGrid could actually handle this case (needs testing),
# but requests doesn't seem to accept a list of tuples for a files field.
# (See the MailgunBackend version for a different approach that might work.)
# (See the Mailgun EmailBackend version for a different approach that might work.)
self.unsupported_feature(
"multiple attachments with the same filename ('%s')" % filename if filename
else "multiple unnamed attachments")

View File

@@ -1,7 +1,10 @@
from __future__ import absolute_import # we want the sparkpost package, not our own module
import warnings
from .base import AnymailBaseBackend, BasePayload
from ..exceptions import AnymailAPIError, AnymailImproperlyInstalled, AnymailConfigurationError
from ..exceptions import (AnymailAPIError, AnymailImproperlyInstalled,
AnymailConfigurationError, AnymailDeprecationWarning)
from ..message import AnymailRecipientStatus
from ..utils import get_anymail_setting
@@ -11,14 +14,16 @@ except ImportError:
raise AnymailImproperlyInstalled(missing_package='sparkpost', backend='sparkpost')
class SparkPostBackend(AnymailBaseBackend):
class EmailBackend(AnymailBaseBackend):
"""
SparkPost Email Backend (using python-sparkpost client)
"""
esp_name = "SparkPost"
def __init__(self, **kwargs):
"""Init options from Django settings"""
super(SparkPostBackend, self).__init__(**kwargs)
super(EmailBackend, self).__init__(**kwargs)
# SPARKPOST_API_KEY is optional - library reads from env by default
self.api_key = get_anymail_setting('api_key', esp_name=self.esp_name,
kwargs=kwargs, allow_bare=True, default=None)
@@ -77,6 +82,15 @@ class SparkPostBackend(AnymailBaseBackend):
return {recipient.email: recipient_status for recipient in payload.all_recipients}
# Pre-v0.8 naming (deprecated)
class SparkPostBackend(EmailBackend):
def __init__(self, **kwargs):
warnings.warn(AnymailDeprecationWarning(
"Please update your EMAIL_BACKEND setting to "
"'anymail.backends.sparkpost.EmailBackend'"))
super(SparkPostBackend, self).__init__(**kwargs)
class SparkPostPayload(BasePayload):
def init_payload(self):
self.params = {}

View File

@@ -5,13 +5,15 @@ from .base import AnymailBaseBackend, BasePayload
from ..utils import get_anymail_setting
class TestBackend(AnymailBaseBackend):
class EmailBackend(AnymailBaseBackend):
"""
Anymail backend that doesn't do anything.
Used for testing Anymail common backend functionality.
"""
esp_name = "Test"
def __init__(self, *args, **kwargs):
# Init options from Django settings
esp_name = self.esp_name
@@ -19,7 +21,7 @@ class TestBackend(AnymailBaseBackend):
kwargs=kwargs, allow_bare=True)
self.recorded_send_params = get_anymail_setting('recorded_send_params', default=[],
esp_name=esp_name, kwargs=kwargs)
super(TestBackend, self).__init__(*args, **kwargs)
super(EmailBackend, self).__init__(*args, **kwargs)
def build_message_payload(self, message, defaults):
return TestPayload(backend=self, message=message, defaults=defaults)
@@ -47,6 +49,14 @@ class TestBackend(AnymailBaseBackend):
raise AnymailAPIError('Unparsable test response')
# Pre-v0.8 naming (immediately deprecated for this undocumented test feature)
class TestBackend(object):
def __init__(self, **kwargs):
raise NotImplementedError(
"Anymail's (undocumented) TestBackend has been renamed to "
"'anymail.backends.test.EmailBackend'")
class TestPayload(BasePayload):
# For test purposes, just keep a dict of the params we've received.
# (This approach is also useful for native API backends -- think of

View File

@@ -175,3 +175,7 @@ class AnymailWarning(Warning):
class AnymailInsecureWebhookWarning(AnymailWarning):
"""Warns when webhook configured without any validation"""
class AnymailDeprecationWarning(AnymailWarning, DeprecationWarning):
"""Warning for deprecated Anymail features"""

View File

@@ -17,10 +17,9 @@ To use Anymail's Mailgun backend, set:
.. code-block:: python
EMAIL_BACKEND = "anymail.backends.mailgun.MailgunBackend"
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
in your settings.py. (Watch your capitalization: Mailgun spells their name with a
lowercase "g", so Anymail does too.)
in your settings.py.
.. setting:: ANYMAIL_MAILGUN_API_KEY

View File

@@ -33,7 +33,7 @@ To use Anymail's Mandrill backend, set:
.. code-block:: python
EMAIL_BACKEND = "anymail.backends.mandrill.MandrillBackend"
EMAIL_BACKEND = "anymail.backends.mandrill.EmailBackend"
in your settings.py.

View File

@@ -19,10 +19,9 @@ To use Anymail's Postmark backend, set:
.. code-block:: python
EMAIL_BACKEND = "anymail.backends.postmark.PostmarkBackend"
EMAIL_BACKEND = "anymail.backends.postmark.EmailBackend"
in your settings.py. (Watch your capitalization: Postmark spells their name with a
lowercase "m", so Anymail does too.)
in your settings.py.
.. setting:: ANYMAIL_POSTMARK_SERVER_TOKEN

View File

@@ -34,10 +34,9 @@ To use Anymail's SendGrid backend, set:
.. code-block:: python
EMAIL_BACKEND = "anymail.backends.sendgrid.SendGridBackend"
EMAIL_BACKEND = "anymail.backends.sendgrid.EmailBackend"
in your settings.py. (Watch your capitalization: SendGrid spells
their name with an uppercase "G", so Anymail does too.)
in your settings.py.
.. setting:: ANYMAIL_SENDGRID_API_KEY

View File

@@ -33,10 +33,9 @@ To use Anymail's SparkPost backend, set:
.. code-block:: python
EMAIL_BACKEND = "anymail.backends.sparkpost.SparkPostBackend"
EMAIL_BACKEND = "anymail.backends.sparkpost.EmailBackend"
in your settings.py. (Watch your capitalization: SparkPost spells
their name with an inner capital "P", so Anymail does too.)
in your settings.py.
.. setting:: ANYMAIL_SPARKPOST_API_KEY

View File

@@ -54,7 +54,7 @@ To use Anymail for sending email, edit your Django project's :file:`settings.py`
.. code-block:: python
EMAIL_BACKEND = "anymail.backends.mailgun.MailgunBackend"
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
(:setting:`EMAIL_BACKEND` sets Django's default for sending emails; you can also
use :ref:`multiple Anymail backends <multiple-backends>` to send particular

View File

@@ -18,7 +18,7 @@ but send admin emails directly through an SMTP server:
from django.core.mail import send_mail, get_connection
# send_mail connection defaults to the settings EMAIL_BACKEND, which
# we've set to Anymail's MailgunBackend. This will be sent using Mailgun:
# we've set to Anymail's Mailgun EmailBackend. This will be sent using Mailgun:
send_mail("Thanks", "We sent your order", "sales@example.com", ["customer@example.com"])
# Get a connection to an SMTP backend, and send using that instead:
@@ -27,13 +27,13 @@ but send admin emails directly through an SMTP server:
connection=smtp_backend)
# You can even use multiple Anymail backends in the same app:
sendgrid_backend = get_connection('anymail.backends.sendgrid.SendGridBackend')
sendgrid_backend = get_connection('anymail.backends.sendgrid.EmailBackend')
send_mail("Password reset", "Here you go", "noreply@example.com", ["user@example.com"],
connection=sendgrid_backend)
# You can override settings.py settings with kwargs to get_connection.
# This example supplies credentials to use a SendGrid subuser acccount:
alt_sendgrid_backend = get_connection('anymail.backends.sendgrid.SendGridBackend',
alt_sendgrid_backend = get_connection('anymail.backends.sendgrid.EmailBackend',
username='marketing_subuser', password='abc123')
send_mail("Here's that info", "you wanted", "marketing@example.com", ["prospect@example.com"],
connection=alt_sendgrid_backend)

View File

@@ -19,11 +19,11 @@ from .utils import AnymailTestMixin
recorded_send_params = []
@override_settings(EMAIL_BACKEND='anymail.backends.test.TestBackend',
ANYMAIL_TEST_SAMPLE_SETTING='sample', # required TestBackend setting
@override_settings(EMAIL_BACKEND='anymail.backends.test.EmailBackend',
ANYMAIL_TEST_SAMPLE_SETTING='sample', # required test EmailBackend setting
ANYMAIL_TEST_RECORDED_SEND_PARAMS=recorded_send_params)
class TestBackendTestCase(SimpleTestCase, AnymailTestMixin):
"""Base TestCase using Anymail's TestBackend"""
"""Base TestCase using Anymail's Test EmailBackend"""
def setUp(self):
super(TestBackendTestCase, self).setUp()
@@ -42,8 +42,8 @@ class TestBackendTestCase(SimpleTestCase, AnymailTestMixin):
return recorded_send_params[-1]
@override_settings(EMAIL_BACKEND='anymail.backends.test.TestBackend') # but no ANYMAIL settings overrides
class BackendSettingsTests(SimpleTestCase, AnymailTestMixin): # (so not TestBackendTestCase)
@override_settings(EMAIL_BACKEND='anymail.backends.test.EmailBackend') # but no ANYMAIL settings overrides
class BackendSettingsTests(SimpleTestCase, AnymailTestMixin): # (so not TestBackendTestCase)
"""Test settings initializations for Anymail EmailBackends"""
@override_settings(ANYMAIL={'TEST_SAMPLE_SETTING': 'setting_from_anymail_settings'})
@@ -97,7 +97,7 @@ class UnsupportedFeatureTests(TestBackendTestCase):
def test_unsupported_feature(self):
"""Unsupported features raise AnymailUnsupportedFeature"""
# TestBackend doesn't support non-HTML alternative parts
# Test EmailBackend doesn't support non-HTML alternative parts
self.message.attach_alternative(b'FAKE_MP3_DATA', 'audio/mpeg')
with self.assertRaises(AnymailUnsupportedFeature):
self.message.send()
@@ -135,10 +135,10 @@ class SendDefaultsTests(TestBackendTestCase):
self.assertEqual(params['tags'], ['globaltag'])
self.assertEqual(params['template_id'], 'my-template')
self.assertEqual(params['track_clicks'], True)
self.assertEqual(params['globalextra'], 'globalsetting') # TestBackend merges esp_extra into params
self.assertEqual(params['globalextra'], 'globalsetting') # Test EmailBackend merges esp_extra into params
@override_settings(ANYMAIL={
'TEST_SEND_DEFAULTS': { # "TEST" is the name of the TestBackend's ESP
'TEST_SEND_DEFAULTS': { # "TEST" is the name of the Test EmailBackend's ESP
'metadata': {'global': 'espvalue'},
'tags': ['esptag'],
'track_opens': False,
@@ -152,7 +152,7 @@ class SendDefaultsTests(TestBackendTestCase):
self.assertEqual(params['metadata'], {'global': 'espvalue'})
self.assertEqual(params['tags'], ['esptag'])
self.assertEqual(params['track_opens'], False)
self.assertEqual(params['globalextra'], 'espsetting') # TestBackend merges esp_extra into params
self.assertEqual(params['globalextra'], 'espsetting') # Test EmailBackend merges esp_extra into params
@override_settings(ANYMAIL={
'SEND_DEFAULTS': {
@@ -199,7 +199,7 @@ class SendDefaultsTests(TestBackendTestCase):
'template_id': 'global-template',
'esp_extra': {'globalextra': 'globalsetting'},
},
'TEST_SEND_DEFAULTS': { # "TEST" is the name of the TestBackend's ESP
'TEST_SEND_DEFAULTS': { # "TEST" is the name of the Test EmailBackend's ESP
'merge_global_data': {'esp': 'espmerge'},
'metadata': {'esp': 'espvalue'},
'tags': ['esptag'],

View File

@@ -17,7 +17,7 @@ from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharin
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin
@override_settings(EMAIL_BACKEND='anymail.backends.mailgun.MailgunBackend',
@override_settings(EMAIL_BACKEND='anymail.backends.mailgun.EmailBackend',
ANYMAIL={'MAILGUN_API_KEY': 'test_api_key'})
class MailgunBackendMockAPITestCase(RequestsBackendMockAPITestCase):
DEFAULT_RAW_RESPONSE = b"""{
@@ -483,7 +483,7 @@ class MailgunBackendSessionSharingTestCase(SessionSharingTestCasesMixin, Mailgun
pass # tests are defined in the mixin
@override_settings(EMAIL_BACKEND="anymail.backends.mailgun.MailgunBackend")
@override_settings(EMAIL_BACKEND="anymail.backends.mailgun.EmailBackend")
class MailgunBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
"""Test ESP backend without required settings in place"""
@@ -494,3 +494,13 @@ class MailgunBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
# Make sure the error mentions MAILGUN_API_KEY and ANYMAIL_MAILGUN_API_KEY
self.assertRegex(errmsg, r'\bMAILGUN_API_KEY\b')
self.assertRegex(errmsg, r'\bANYMAIL_MAILGUN_API_KEY\b')
class MailgunBackendDeprecationTests(MailgunBackendMockAPITestCase):
@override_settings(EMAIL_BACKEND='anymail.backends.mailgun.MailgunBackend')
def test_renamed_backend_warning(self):
# ...mailgun.MailgunBackend --> ...mailgun.EmailBackend
with self.assertWarnsRegex(DeprecationWarning,
r'anymail\.backends\.mailgun\.EmailBackend'):
self.message.send()

View File

@@ -24,7 +24,7 @@ MAILGUN_TEST_DOMAIN = os.getenv('MAILGUN_TEST_DOMAIN')
@override_settings(ANYMAIL={'MAILGUN_API_KEY': MAILGUN_TEST_API_KEY,
'MAILGUN_SENDER_DOMAIN': MAILGUN_TEST_DOMAIN,
'MAILGUN_SEND_DEFAULTS': {'esp_extra': {'o:testmode': 'yes'}}},
EMAIL_BACKEND="anymail.backends.mailgun.MailgunBackend")
EMAIL_BACKEND="anymail.backends.mailgun.EmailBackend")
class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
"""Mailgun API integration tests

View File

@@ -19,7 +19,7 @@ from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharin
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin, decode_att
@override_settings(EMAIL_BACKEND='anymail.backends.mandrill.MandrillBackend',
@override_settings(EMAIL_BACKEND='anymail.backends.mandrill.EmailBackend',
ANYMAIL={'MANDRILL_API_KEY': 'test_api_key'})
class MandrillBackendMockAPITestCase(RequestsBackendMockAPITestCase):
DEFAULT_RAW_RESPONSE = b"""[{
@@ -588,7 +588,7 @@ class MandrillBackendSessionSharingTestCase(SessionSharingTestCasesMixin, Mandri
pass # tests are defined in the mixin
@override_settings(EMAIL_BACKEND="anymail.backends.mandrill.MandrillBackend")
@override_settings(EMAIL_BACKEND="anymail.backends.mandrill.EmailBackend")
class MandrillBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
"""Test backend without required settings"""
@@ -598,3 +598,12 @@ class MandrillBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin)
errmsg = str(cm.exception)
self.assertRegex(errmsg, r'\bMANDRILL_API_KEY\b')
self.assertRegex(errmsg, r'\bANYMAIL_MANDRILL_API_KEY\b')
class MandrillBackendDeprecationTests(MandrillBackendMockAPITestCase):
@override_settings(EMAIL_BACKEND='anymail.backends.mandrill.MandrillBackend')
def test_renamed_backend_warning(self):
# ...mandrill.MandrillBackend --> ...mandrill.EmailBackend
with self.assertWarnsRegex(DeprecationWarning,
r'anymail\.backends\.mandrill\.EmailBackend'):
self.message.send()

View File

@@ -17,7 +17,7 @@ MANDRILL_TEST_API_KEY = os.getenv('MANDRILL_TEST_API_KEY')
@unittest.skipUnless(MANDRILL_TEST_API_KEY,
"Set MANDRILL_TEST_API_KEY environment variable to run integration tests")
@override_settings(MANDRILL_API_KEY=MANDRILL_TEST_API_KEY,
EMAIL_BACKEND="anymail.backends.mandrill.MandrillBackend")
EMAIL_BACKEND="anymail.backends.mandrill.EmailBackend")
class MandrillBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
"""Mandrill API integration tests

View File

@@ -18,7 +18,7 @@ from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharin
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin, decode_att
@override_settings(EMAIL_BACKEND='anymail.backends.postmark.PostmarkBackend',
@override_settings(EMAIL_BACKEND='anymail.backends.postmark.EmailBackend',
ANYMAIL={'POSTMARK_SERVER_TOKEN': 'test_server_token'})
class PostmarkBackendMockAPITestCase(RequestsBackendMockAPITestCase):
DEFAULT_RAW_RESPONSE = b"""{
@@ -560,7 +560,7 @@ class PostmarkBackendSessionSharingTestCase(SessionSharingTestCasesMixin, Postma
pass # tests are defined in the mixin
@override_settings(EMAIL_BACKEND="anymail.backends.postmark.PostmarkBackend")
@override_settings(EMAIL_BACKEND="anymail.backends.postmark.EmailBackend")
class PostmarkBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
"""Test ESP backend without required settings in place"""
@@ -570,3 +570,12 @@ class PostmarkBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin)
errmsg = str(cm.exception)
self.assertRegex(errmsg, r'\bPOSTMARK_SERVER_TOKEN\b')
self.assertRegex(errmsg, r'\bANYMAIL_POSTMARK_SERVER_TOKEN\b')
class PostmarkBackendDeprecationTests(PostmarkBackendMockAPITestCase):
@override_settings(EMAIL_BACKEND='anymail.backends.postmark.PostmarkBackend')
def test_renamed_backend_warning(self):
# ...postmark.PostmarkBackend --> ...postmark.EmailBackend
with self.assertWarnsRegex(DeprecationWarning,
r'anymail\.backends\.postmark\.EmailBackend'):
self.message.send()

View File

@@ -11,7 +11,7 @@ from .utils import AnymailTestMixin, sample_image_path, RUN_LIVE_TESTS
@unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment")
@override_settings(ANYMAIL_POSTMARK_SERVER_TOKEN="POSTMARK_API_TEST",
EMAIL_BACKEND="anymail.backends.postmark.PostmarkBackend")
EMAIL_BACKEND="anymail.backends.postmark.EmailBackend")
class PostmarkBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
"""Postmark API integration tests

View File

@@ -1,6 +1,6 @@
from django.dispatch import receiver
from anymail.backends.test import TestBackend
from anymail.backends.test import EmailBackend as TestEmailBackend
from anymail.exceptions import AnymailCancelSend, AnymailRecipientsRefused
from anymail.message import AnymailRecipientStatus
from anymail.signals import pre_send, post_send
@@ -16,9 +16,9 @@ class TestPreSendSignal(TestBackendTestCase):
@receiver(pre_send, weak=False)
def handle_pre_send(sender, message, esp_name, **kwargs):
self.assertEqual(self.get_send_count(), 0) # not sent yet
self.assertEqual(sender, TestBackend)
self.assertEqual(sender, TestEmailBackend)
self.assertEqual(message, self.message)
self.assertEqual(esp_name, "Test") # the TestBackend's ESP is named "Test"
self.assertEqual(esp_name, "Test") # the TestEmailBackend's ESP is named "Test"
self.receiver_called = True
self.addCleanup(pre_send.disconnect, receiver=handle_pre_send)
@@ -62,13 +62,13 @@ class TestPostSendSignal(TestBackendTestCase):
@receiver(post_send, weak=False)
def handle_post_send(sender, message, status, esp_name, **kwargs):
self.assertEqual(self.get_send_count(), 1) # already sent
self.assertEqual(sender, TestBackend)
self.assertEqual(sender, TestEmailBackend)
self.assertEqual(message, self.message)
self.assertEqual(status.status, {'sent'})
self.assertEqual(status.message_id, 1) # TestBackend default message_id
self.assertEqual(status.message_id, 1) # TestEmailBackend default message_id
self.assertEqual(status.recipients['to@example.com'].status, 'sent')
self.assertEqual(status.recipients['to@example.com'].message_id, 1)
self.assertEqual(esp_name, "Test") # the TestBackend's ESP is named "Test"
self.assertEqual(esp_name, "Test") # the TestEmailBackend's ESP is named "Test"
self.receiver_called = True
self.addCleanup(post_send.disconnect, receiver=handle_post_send)

View File

@@ -20,7 +20,7 @@ from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharin
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin
@override_settings(EMAIL_BACKEND='anymail.backends.sendgrid.SendGridBackend',
@override_settings(EMAIL_BACKEND='anymail.backends.sendgrid.EmailBackend',
ANYMAIL={'SENDGRID_API_KEY': 'test_api_key'})
class SendGridBackendMockAPITestCase(RequestsBackendMockAPITestCase):
DEFAULT_RAW_RESPONSE = b"" # SendGrid v3 success responses are empty
@@ -619,7 +619,7 @@ class SendGridBackendSessionSharingTestCase(SessionSharingTestCasesMixin, SendGr
pass # tests are defined in the mixin
@override_settings(EMAIL_BACKEND="anymail.backends.sendgrid.SendGridBackend")
@override_settings(EMAIL_BACKEND="anymail.backends.sendgrid.EmailBackend")
class SendGridBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
"""Test ESP backend without required settings in place"""
@@ -628,7 +628,7 @@ class SendGridBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin)
mail.send_mail('Subject', 'Message', 'from@example.com', ['to@example.com'])
@override_settings(EMAIL_BACKEND="anymail.backends.sendgrid.SendGridBackend")
@override_settings(EMAIL_BACKEND="anymail.backends.sendgrid.EmailBackend")
class SendGridBackendDisallowsV2Tests(SimpleTestCase, AnymailTestMixin):
"""Using v2-API-only features should cause errors with v3 backend"""
@@ -645,3 +645,12 @@ class SendGridBackendDisallowsV2Tests(SimpleTestCase, AnymailTestMixin):
message.esp_extra = {'x-smtpapi': {'asm_group_id': 1}}
with self.assertRaisesRegex(AnymailConfigurationError, r'\bsendgrid_v2\.EmailBackend\b'):
message.send()
class SendGridBackendDeprecationTests(SendGridBackendMockAPITestCase):
@override_settings(EMAIL_BACKEND='anymail.backends.sendgrid.SendGridBackend')
def test_renamed_backend_warning(self):
# ...sendgrid.SendGridBackend --> ...sendgrid.EmailBackend
with self.assertWarnsRegex(DeprecationWarning,
r'anymail\.backends\.sendgrid\.EmailBackend'):
self.message.send()

View File

@@ -22,7 +22,7 @@ SENDGRID_TEST_TEMPLATE_ID = os.getenv('SENDGRID_TEST_TEMPLATE_ID')
ANYMAIL_SENDGRID_SEND_DEFAULTS={"esp_extra": {
"mail_settings": {"sandbox_mode": {"enable": True}},
}},
EMAIL_BACKEND="anymail.backends.sendgrid.SendGridBackend")
EMAIL_BACKEND="anymail.backends.sendgrid.EmailBackend")
class SendGridBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
"""SendGrid v3 API integration tests

View File

@@ -21,7 +21,7 @@ from anymail.message import attach_inline_image_file
from .utils import AnymailTestMixin, decode_att, SAMPLE_IMAGE_FILENAME, sample_image_path, sample_image_content
@override_settings(EMAIL_BACKEND='anymail.backends.sparkpost.SparkPostBackend',
@override_settings(EMAIL_BACKEND='anymail.backends.sparkpost.EmailBackend',
ANYMAIL={'SPARKPOST_API_KEY': 'test_api_key'})
class SparkPostBackendMockAPITestCase(SimpleTestCase, AnymailTestMixin):
"""TestCase that uses SparkPostEmailBackend with a mocked transmissions.send API"""
@@ -559,7 +559,7 @@ class SparkPostBackendRecipientsRefusedTests(SparkPostBackendMockAPITestCase):
self.assertEqual(sent, 1) # refused message is included in sent count
@override_settings(EMAIL_BACKEND="anymail.backends.sparkpost.SparkPostBackend")
@override_settings(EMAIL_BACKEND="anymail.backends.sparkpost.EmailBackend")
class SparkPostBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
"""Test ESP backend without required settings in place"""
@@ -580,3 +580,12 @@ class SparkPostBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin
# Poke into implementation details to verify:
self.assertIsNone(conn.api_key) # Anymail prop
self.assertEqual(conn.sp.api_key, 'key_from_environment') # SparkPost prop
class SparkPostBackendDeprecationTests(SparkPostBackendMockAPITestCase):
@override_settings(EMAIL_BACKEND='anymail.backends.sparkpost.SparkPostBackend')
def test_renamed_backend_warning(self):
# ...sparkpost.SparkPostBackend --> ...sparkpost.EmailBackend
with self.assertWarnsRegex(DeprecationWarning,
r'anymail\.backends\.sparkpost\.EmailBackend'):
self.message.send()

View File

@@ -18,7 +18,7 @@ SPARKPOST_TEST_API_KEY = os.getenv('SPARKPOST_TEST_API_KEY')
"Set SPARKPOST_TEST_API_KEY environment variable "
"to run SparkPost integration tests")
@override_settings(ANYMAIL_SPARKPOST_API_KEY=SPARKPOST_TEST_API_KEY,
EMAIL_BACKEND="anymail.backends.sparkpost.SparkPostBackend")
EMAIL_BACKEND="anymail.backends.sparkpost.EmailBackend")
class SparkPostBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
"""SparkPost API integration tests