File tree Expand file tree Collapse file tree 7 files changed +75
-4
lines changed Expand file tree Collapse file tree 7 files changed +75
-4
lines changed Original file line number Diff line number Diff line change 3333Fixes
3434~~~~~
3535
36+ * Fix misleading error messages when sending with ``fail_silently=True ``
37+ and session creation fails (e.g., with Amazon SES backend and missing
38+ credentials). (Thanks to `@technolingo `_.)
39+
3640* **Postmark: ** Fix spurious AnymailInvalidAddress in ``message.cc `` when
3741 inbound message has no Cc recipients. (Thanks to `@Ecno92 `_.)
3842
@@ -1455,6 +1459,7 @@ Features
14551459.. _@slinkymanbyday : https://github.com/slinkymanbyday
14561460.. _@swrobel : https://github.com/swrobel
14571461.. _@tcourtqtm : https://github.com/tcourtqtm
1462+ .. _@technolingo : https://github.com/technolingo
14581463.. _@Thorbenl : https://github.com/Thorbenl
14591464.. _@tiltec : https://github.com/tiltec
14601465.. _@tim-schilling : https://github.com/tim-schilling
Original file line number Diff line number Diff line change @@ -59,7 +59,7 @@ def open(self):
5959 self .client = boto3 .session .Session (** self .session_params ).client (
6060 "ses" , ** self .client_params
6161 )
62- except BOTO_BASE_ERRORS :
62+ except Exception :
6363 if not self .fail_silently :
6464 raise
6565 else :
@@ -71,6 +71,22 @@ def close(self):
7171 # self.client.close() # boto3 doesn't support (or require) client shutdown
7272 self .client = None
7373
74+ def _send (self , message ):
75+ if self .client :
76+ return super ()._send (message )
77+ elif self .fail_silently :
78+ # (Probably missing boto3 credentials in open().)
79+ return False
80+ else :
81+ class_name = self .__class__ .__name__
82+ raise RuntimeError (
83+ "boto3 Session has not been opened in {class_name}._send. "
84+ "(This is either an implementation error in {class_name}, "
85+ "or you are incorrectly calling _send directly.)" .format (
86+ class_name = class_name
87+ )
88+ )
89+
7490 def build_message_payload (self , message , defaults ):
7591 # The SES SendRawEmail and SendBulkTemplatedEmail calls have
7692 # very different signatures, so use a custom payload for each
Original file line number Diff line number Diff line change @@ -63,7 +63,7 @@ def open(self):
6363 self .client = boto3 .session .Session (** self .session_params ).client (
6464 "sesv2" , ** self .client_params
6565 )
66- except BOTO_BASE_ERRORS :
66+ except Exception :
6767 if not self .fail_silently :
6868 raise
6969 else :
@@ -75,6 +75,22 @@ def close(self):
7575 self .client .close ()
7676 self .client = None
7777
78+ def _send (self , message ):
79+ if self .client :
80+ return super ()._send (message )
81+ elif self .fail_silently :
82+ # (Probably missing boto3 credentials in open().)
83+ return False
84+ else :
85+ class_name = self .__class__ .__name__
86+ raise RuntimeError (
87+ "boto3 Session has not been opened in {class_name}._send. "
88+ "(This is either an implementation error in {class_name}, "
89+ "or you are incorrectly calling _send directly.)" .format (
90+ class_name = class_name
91+ )
92+ )
93+
7894 def build_message_payload (self , message , defaults ):
7995 if getattr (message , "template_id" , UNSET ) is not UNSET :
8096 # For simplicity, use SESv2 SendBulkEmail for all templated messages
Original file line number Diff line number Diff line change @@ -47,7 +47,12 @@ def close(self):
4747 self .session = None
4848
4949 def _send (self , message ):
50- if self .session is None :
50+ if self .session :
51+ return super ()._send (message )
52+ elif self .fail_silently :
53+ # create_session failed
54+ return False
55+ else :
5156 class_name = self .__class__ .__name__
5257 raise RuntimeError (
5358 "Session has not been opened in {class_name}._send. "
@@ -56,7 +61,6 @@ def _send(self, message):
5661 class_name = class_name
5762 )
5863 )
59- return super ()._send (message )
6064
6165 def create_session (self ):
6266 """Create the requests.Session object used by this instance of the backend.
Original file line number Diff line number Diff line change @@ -393,6 +393,16 @@ def test_api_failure_fail_silently(self):
393393 sent = self .message .send (fail_silently = True )
394394 self .assertEqual (sent , 0 )
395395
396+ def test_session_failure_fail_silently (self ):
397+ # Make sure fail_silently is respected if boto3.Session creation fails
398+ # (e.g., due to invalid or missing credentials)
399+ from botocore .exceptions import NoCredentialsError
400+
401+ self .mock_session .side_effect = NoCredentialsError ()
402+
403+ sent = self .message .send (fail_silently = True )
404+ self .assertEqual (sent , 0 )
405+
396406 def test_prevents_header_injection (self ):
397407 # Since we build the raw MIME message, we're responsible for preventing header
398408 # injection. django.core.mail.EmailMessage.message() implements most of that
Original file line number Diff line number Diff line change @@ -403,6 +403,16 @@ def test_api_failure_fail_silently(self):
403403 sent = self .message .send (fail_silently = True )
404404 self .assertEqual (sent , 0 )
405405
406+ def test_session_failure_fail_silently (self ):
407+ # Make sure fail_silently is respected if boto3.Session creation fails
408+ # (e.g., due to invalid or missing credentials)
409+ from botocore .exceptions import NoCredentialsError
410+
411+ self .mock_session .side_effect = NoCredentialsError ()
412+
413+ sent = self .message .send (fail_silently = True )
414+ self .assertEqual (sent , 0 )
415+
406416 def test_prevents_header_injection (self ):
407417 # Since we build the raw MIME message, we're responsible for preventing header
408418 # injection. django.core.mail.EmailMessage.message() implements most of that
Original file line number Diff line number Diff line change 1+ from unittest import mock
2+
13from django .test import SimpleTestCase , override_settings , tag
24
35from anymail .backends .base_requests import AnymailRequestsBackend , RequestsPayload
@@ -69,6 +71,14 @@ def test_timeout_setting(self):
6971 timeout = self .get_api_call_arg ("timeout" )
7072 self .assertEqual (timeout , 5 )
7173
74+ @mock .patch (f"{ __name__ } .MinimalRequestsBackend.create_session" )
75+ def test_create_session_error_fail_silently (self , mock_create_session ):
76+ # If create_session fails and fail_silently is True,
77+ # make sure _send doesn't raise a misleading error.
78+ mock_create_session .side_effect = ValueError ("couldn't create session" )
79+ sent = self .message .send (fail_silently = True )
80+ self .assertEqual (sent , 0 )
81+
7282
7383@tag ("live" )
7484@override_settings (EMAIL_BACKEND = "tests.test_base_backends.MinimalRequestsBackend" )
You can’t perform that action at this time.
0 commit comments