diff --git a/post_office/connections.py b/post_office/connections.py index 435749ee..c6f98df7 100644 --- a/post_office/connections.py +++ b/post_office/connections.py @@ -40,6 +40,11 @@ def all(self): def close(self): for connection in self.all(): connection.close() + # Evict closed connections so the next __getitem__ reopens them. + # Keeping closed connections cached breaks backends (e.g. Amazon SES) + # whose close() nulls out internal clients — subsequent batches would + # hand workers a dead connection and race inside send_messages. + self._connections.connections = {} connections = ConnectionHandler() diff --git a/tests/test_connections.py b/tests/test_connections.py index d48d0891..2d21dbca 100644 --- a/tests/test_connections.py +++ b/tests/test_connections.py @@ -10,3 +10,13 @@ def test_get_connection(self): # Ensure ConnectionHandler returns the right connection self.assertTrue(isinstance(connections['error'], ErrorRaisingBackend)) self.assertTrue(isinstance(connections['locmem'], backends.locmem.EmailBackend)) + + def test_close_evicts_cache(self): + # Ensure connections.close() clears the cache so the next __getitem__ + # yields a freshly-opened connection. Without this, backends that null + # out their client on close() (e.g. Amazon SES) would hand out dead + # connections on subsequent batches. + first = connections['locmem'] + connections.close() + second = connections['locmem'] + self.assertIsNot(first, second) diff --git a/tests/test_models.py b/tests/test_models.py index b1d62974..e410a54a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -10,6 +10,7 @@ from django.core.mail import EmailMessage, EmailMultiAlternatives from django.forms.models import modelform_factory from django.test import TestCase +from django.test.utils import override_settings from django.utils import timezone from post_office.models import Email, Log, PRIORITY, STATUS, EmailTemplate, Attachment @@ -97,9 +98,8 @@ def test_dispatch(self): email.dispatch() self.assertEqual(mail.outbox[0].subject, 'Test dispatch') + @override_settings(POST_OFFICE={**settings.POST_OFFICE, 'OVERRIDE_RECIPIENTS': ['override@gmail.com']}) def test_dispatch_with_override_recipients(self): - previous_settings = settings.POST_OFFICE - setattr(settings, 'POST_OFFICE', {'OVERRIDE_RECIPIENTS': ['override@gmail.com']}) email = Email.objects.create( to=['to@example.com'], from_email='from@example.com', @@ -109,7 +109,6 @@ def test_dispatch_with_override_recipients(self): ) email.dispatch() self.assertEqual(mail.outbox[0].to, ['override@gmail.com']) - settings.POST_OFFICE = previous_settings def test_status_and_log(self): """