From 79288603fb69a4c521ecb90fcfdac17003b45f36 Mon Sep 17 00:00:00 2001 From: medmunds Date: Fri, 20 Jan 2017 15:47:37 -0800 Subject: [PATCH] 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. --- README.rst | 2 +- anymail/backends/base.py | 7 +++++-- anymail/backends/mailgun.py | 18 +++++++++++++++--- anymail/backends/mandrill.py | 17 ++++++++++++++--- anymail/backends/postmark.py | 20 ++++++++++++++++---- anymail/backends/sendgrid.py | 17 ++++++++++++++--- anymail/backends/sendgrid_v2.py | 2 +- anymail/backends/sparkpost.py | 20 +++++++++++++++++--- anymail/backends/test.py | 14 ++++++++++++-- anymail/exceptions.py | 4 ++++ docs/esps/mailgun.rst | 5 ++--- docs/esps/mandrill.rst | 2 +- docs/esps/postmark.rst | 5 ++--- docs/esps/sendgrid.rst | 5 ++--- docs/esps/sparkpost.rst | 5 ++--- docs/installation.rst | 2 +- docs/tips/multiple_backends.rst | 6 +++--- tests/test_general_backend.py | 20 ++++++++++---------- tests/test_mailgun_backend.py | 14 ++++++++++++-- tests/test_mailgun_integration.py | 2 +- tests/test_mandrill_backend.py | 13 +++++++++++-- tests/test_mandrill_integration.py | 2 +- tests/test_postmark_backend.py | 13 +++++++++++-- tests/test_postmark_integration.py | 2 +- tests/test_send_signals.py | 12 ++++++------ tests/test_sendgrid_backend.py | 15 ++++++++++++--- tests/test_sendgrid_integration.py | 2 +- tests/test_sparkpost_backend.py | 13 +++++++++++-- tests/test_sparkpost_integration.py | 2 +- 29 files changed, 190 insertions(+), 71 deletions(-) diff --git a/README.rst b/README.rst index 40d8b15..2308118 100644 --- a/README.rst +++ b/README.rst @@ -101,7 +101,7 @@ or SparkPost or any other supported ESP where you see "mailgun": "MAILGUN_API_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 diff --git a/anymail/backends/base.py b/anymail/backends/base.py index bf13c80..a4bf413 100644 --- a/anymail/backends/base.py +++ b/anymail/backends/base.py @@ -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): diff --git a/anymail/backends/mailgun.py b/anymail/backends/mailgun.py index 82647e7..34a5c9e 100644 --- a/anymail/backends/mailgun.py +++ b/anymail/backends/mailgun.py @@ -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): diff --git a/anymail/backends/mandrill.py b/anymail/backends/mandrill.py index be203e4..c61f7ff 100644 --- a/anymail/backends/mandrill.py +++ b/anymail/backends/mandrill.py @@ -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""" diff --git a/anymail/backends/postmark.py b/anymail/backends/postmark.py index 55819bc..48e523d 100644 --- a/anymail/backends/postmark.py +++ b/anymail/backends/postmark.py @@ -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): diff --git a/anymail/backends/sendgrid.py b/anymail/backends/sendgrid.py index 423685d..6f8f605 100644 --- a/anymail/backends/sendgrid.py +++ b/anymail/backends/sendgrid.py @@ -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): diff --git a/anymail/backends/sendgrid_v2.py b/anymail/backends/sendgrid_v2.py index 045acca..c0e2c8d 100644 --- a/anymail/backends/sendgrid_v2.py +++ b/anymail/backends/sendgrid_v2.py @@ -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") diff --git a/anymail/backends/sparkpost.py b/anymail/backends/sparkpost.py index dad7dfb..6d1d8f6 100644 --- a/anymail/backends/sparkpost.py +++ b/anymail/backends/sparkpost.py @@ -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 = {} diff --git a/anymail/backends/test.py b/anymail/backends/test.py index 70b022c..0c3cbac 100644 --- a/anymail/backends/test.py +++ b/anymail/backends/test.py @@ -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 diff --git a/anymail/exceptions.py b/anymail/exceptions.py index c0af427..d49a9df 100644 --- a/anymail/exceptions.py +++ b/anymail/exceptions.py @@ -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""" diff --git a/docs/esps/mailgun.rst b/docs/esps/mailgun.rst index 2afb559..a9c1f0e 100644 --- a/docs/esps/mailgun.rst +++ b/docs/esps/mailgun.rst @@ -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 diff --git a/docs/esps/mandrill.rst b/docs/esps/mandrill.rst index 39a1e31..5fd5353 100644 --- a/docs/esps/mandrill.rst +++ b/docs/esps/mandrill.rst @@ -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. diff --git a/docs/esps/postmark.rst b/docs/esps/postmark.rst index e00b165..9ce25f0 100644 --- a/docs/esps/postmark.rst +++ b/docs/esps/postmark.rst @@ -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 diff --git a/docs/esps/sendgrid.rst b/docs/esps/sendgrid.rst index 4844ad8..8e0def9 100644 --- a/docs/esps/sendgrid.rst +++ b/docs/esps/sendgrid.rst @@ -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 diff --git a/docs/esps/sparkpost.rst b/docs/esps/sparkpost.rst index 066ae8e..db34d35 100644 --- a/docs/esps/sparkpost.rst +++ b/docs/esps/sparkpost.rst @@ -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 diff --git a/docs/installation.rst b/docs/installation.rst index fb654ab..2a7cebd 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -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 ` to send particular diff --git a/docs/tips/multiple_backends.rst b/docs/tips/multiple_backends.rst index 8c78167..71c9ef5 100644 --- a/docs/tips/multiple_backends.rst +++ b/docs/tips/multiple_backends.rst @@ -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) diff --git a/tests/test_general_backend.py b/tests/test_general_backend.py index 927ff3d..5515891 100644 --- a/tests/test_general_backend.py +++ b/tests/test_general_backend.py @@ -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'], diff --git a/tests/test_mailgun_backend.py b/tests/test_mailgun_backend.py index 83133f6..0672263 100644 --- a/tests/test_mailgun_backend.py +++ b/tests/test_mailgun_backend.py @@ -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() + diff --git a/tests/test_mailgun_integration.py b/tests/test_mailgun_integration.py index 0b3338f..05c49e2 100644 --- a/tests/test_mailgun_integration.py +++ b/tests/test_mailgun_integration.py @@ -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 diff --git a/tests/test_mandrill_backend.py b/tests/test_mandrill_backend.py index 7f11945..9cdaacc 100644 --- a/tests/test_mandrill_backend.py +++ b/tests/test_mandrill_backend.py @@ -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() diff --git a/tests/test_mandrill_integration.py b/tests/test_mandrill_integration.py index ec57907..901fc78 100644 --- a/tests/test_mandrill_integration.py +++ b/tests/test_mandrill_integration.py @@ -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 diff --git a/tests/test_postmark_backend.py b/tests/test_postmark_backend.py index 3a67cfc..c593645 100644 --- a/tests/test_postmark_backend.py +++ b/tests/test_postmark_backend.py @@ -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() diff --git a/tests/test_postmark_integration.py b/tests/test_postmark_integration.py index 05114bb..9597c43 100644 --- a/tests/test_postmark_integration.py +++ b/tests/test_postmark_integration.py @@ -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 diff --git a/tests/test_send_signals.py b/tests/test_send_signals.py index f43073e..3a77c08 100644 --- a/tests/test_send_signals.py +++ b/tests/test_send_signals.py @@ -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) diff --git a/tests/test_sendgrid_backend.py b/tests/test_sendgrid_backend.py index 5de7ad3..1760073 100644 --- a/tests/test_sendgrid_backend.py +++ b/tests/test_sendgrid_backend.py @@ -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() diff --git a/tests/test_sendgrid_integration.py b/tests/test_sendgrid_integration.py index b2ebe09..9f03fd4 100644 --- a/tests/test_sendgrid_integration.py +++ b/tests/test_sendgrid_integration.py @@ -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 diff --git a/tests/test_sparkpost_backend.py b/tests/test_sparkpost_backend.py index 8f108a9..b988dc0 100644 --- a/tests/test_sparkpost_backend.py +++ b/tests/test_sparkpost_backend.py @@ -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() diff --git a/tests/test_sparkpost_integration.py b/tests/test_sparkpost_integration.py index 1125ea5..bb6be2e 100644 --- a/tests/test_sparkpost_integration.py +++ b/tests/test_sparkpost_integration.py @@ -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