Skip to content

Commit 2333d56

Browse files
prafulgulanijacobtylerwalls
authored andcommitted
Fixed #36894 -- Added TypeError for conflicting arguments in mail APIs.
A TypeError is now raised if fail_silently=True, auth_user, or auth_password are provided along a connection. Updated AdminEmailHandler in django.utils.log to remove redundant fail_silently=True. Thanks Mike Edmunds for the report and Jacob Tyler Walls for the review.
1 parent 455e787 commit 2333d56

7 files changed

Lines changed: 177 additions & 9 deletions

File tree

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,7 @@ answer newbie questions, and generally made Django that much better:
865865
plisk
866866
polpak@yahoo.com
867867
pradeep.gowda@gmail.com
868+
Praful Gulani <https://github.com/prafulgulani>
868869
Prashant Pandey <https://prashantpandey9.in>
869870
Preston Holmes <preston@ptone.com>
870871
Preston Timmons <prestontimmons@gmail.com>

django/core/mail/__init__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,17 @@ def send_mail(
9494
Note: The API for this method is frozen. New code wanting to extend the
9595
functionality should use the EmailMessage class directly.
9696
"""
97+
if connection is not None:
98+
if fail_silently:
99+
raise TypeError(
100+
"fail_silently cannot be used with a connection. "
101+
"Pass fail_silently to get_connection() instead."
102+
)
103+
if auth_user is not None or auth_password is not None:
104+
raise TypeError(
105+
"auth_user and auth_password cannot be used with a connection. "
106+
"Pass auth_user and auth_password to get_connection() instead."
107+
)
97108
connection = connection or get_connection(
98109
username=auth_user,
99110
password=auth_password,
@@ -137,6 +148,17 @@ def send_mass_mail(
137148
Note: The API for this method is frozen. New code wanting to extend the
138149
functionality should use the EmailMessage class directly.
139150
"""
151+
if connection is not None:
152+
if fail_silently:
153+
raise TypeError(
154+
"fail_silently cannot be used with a connection. "
155+
"Pass fail_silently to get_connection() instead."
156+
)
157+
if auth_user is not None or auth_password is not None:
158+
raise TypeError(
159+
"auth_user and auth_password cannot be used with a connection. "
160+
"Pass auth_user and auth_password to get_connection() instead."
161+
)
140162
connection = connection or get_connection(
141163
username=auth_user,
142164
password=auth_password,
@@ -158,6 +180,11 @@ def _send_server_message(
158180
fail_silently=False,
159181
connection=None,
160182
):
183+
if connection is not None and fail_silently:
184+
raise TypeError(
185+
"fail_silently cannot be used with a connection. "
186+
"Pass fail_silently to get_connection() instead."
187+
)
161188
recipients = getattr(settings, setting_name)
162189
if not recipients:
163190
return

django/core/mail/message.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,13 @@ def send(self, fail_silently=False):
355355
# Don't bother creating the network connection if there's nobody to
356356
# send to.
357357
return 0
358+
359+
if fail_silently and self.connection:
360+
raise TypeError(
361+
"fail_silently cannot be used with a connection. "
362+
"Pass fail_silently to get_connection() instead."
363+
)
364+
358365
return self.get_connection(fail_silently).send_messages([self])
359366

360367
def attach(self, filename=None, content=None, mimetype=None):

django/utils/log.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def emit(self, record):
132132
reporter.get_traceback_text(),
133133
)
134134
html_message = reporter.get_traceback_html() if self.include_html else None
135-
self.send_mail(subject, message, fail_silently=True, html_message=html_message)
135+
self.send_mail(subject, message, html_message=html_message)
136136

137137
def send_mail(self, subject, message, *args, **kwargs):
138138
mail.mail_admins(

docs/releases/6.1.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,10 @@ short-term maintenance releases. Django 6.1 supports MariaDB 10.11 and higher.
457457
Miscellaneous
458458
-------------
459459

460+
* Providing ``fail_silently=True``, ``auth_user``, or ``auth_password`` to mail
461+
sending functions (such as :func:`~django.core.mail.send_mail`) while also
462+
providing a ``connection`` now raises a ``TypeError``.
463+
460464
* :class:`~django.contrib.contenttypes.fields.GenericForeignKey` now uses a
461465
separate descriptor class: the private ``GenericForeignKeyDescriptor``.
462466

docs/topics/email.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ can be ``0`` or ``1`` since it can only send one message).
120120
Passing ``fail_silently`` and later parameters as positional arguments is
121121
deprecated.
122122

123+
.. versionchanged:: 6.1
124+
125+
Older versions ignored ``fail_silently=True``, ``auth_user``,
126+
and ``auth_password`` when a ``connection`` was also provided.
127+
This now raises a ``TypeError``.
128+
123129
``send_mass_mail()``
124130
====================
125131

@@ -164,6 +170,13 @@ The return value will be the number of successfully delivered messages.
164170
Passing ``fail_silently`` and later parameters as positional arguments is
165171
deprecated.
166172

173+
174+
.. versionchanged:: 6.1
175+
176+
Older versions ignored ``fail_silently=True``, ``auth_user``,
177+
and ``auth_password`` when a ``connection`` was also provided.
178+
This now raises a ``TypeError``.
179+
167180
``send_mass_mail()`` vs. ``send_mail()``
168181
----------------------------------------
169182

@@ -198,6 +211,11 @@ If ``html_message`` is provided, the resulting email will be a
198211
Passing ``fail_silently`` and later parameters as positional arguments is
199212
deprecated.
200213

214+
.. versionchanged:: 6.1
215+
216+
Older versions ignored ``fail_silently=True`` when a ``connection``
217+
was also provided. This now raises a ``TypeError``.
218+
201219
``mail_managers()``
202220
===================
203221

@@ -212,6 +230,11 @@ setting.
212230
Passing ``fail_silently`` and later parameters as positional arguments is
213231
deprecated.
214232

233+
.. versionchanged:: 6.1
234+
235+
Older versions ignored ``fail_silently=True`` when a ``connection``
236+
was also provided. This now raises a ``TypeError``.
237+
215238
Examples
216239
========
217240

@@ -403,6 +426,11 @@ email backend API :ref:`provides an alternative
403426
an exception. It will return ``1`` if the message was sent
404427
successfully, otherwise ``0``.
405428

429+
.. versionchanged:: 6.1
430+
431+
Older versions ignored ``fail_silently=True`` when a ``connection``
432+
was also provided. This now raises a ``TypeError``.
433+
406434
.. method:: message(policy=email.policy.default)
407435

408436
Constructs and returns a Python :class:`email.message.EmailMessage`

tests/mail/tests.py

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,6 +1999,107 @@ def test_message_policy_compat32(self):
19991999
message.as_string(policy=policy.compat32),
20002000
)
20012001

2002+
def test_send_mail_fail_silently_conflict(self):
2003+
msg = (
2004+
"fail_silently cannot be used with a connection. "
2005+
"Pass fail_silently to get_connection() instead."
2006+
)
2007+
with self.assertRaisesMessage(TypeError, msg):
2008+
mail.send_mail(
2009+
"Subject",
2010+
"Body",
2011+
"from@example.com",
2012+
["to@example.com"],
2013+
fail_silently=True,
2014+
connection=mail.get_connection(),
2015+
)
2016+
2017+
def test_send_mail_auth_conflict(self):
2018+
msg = (
2019+
"auth_user and auth_password cannot be used with a connection. "
2020+
"Pass auth_user and auth_password to get_connection() instead."
2021+
)
2022+
for param in ["auth_user", "auth_password"]:
2023+
with (
2024+
self.subTest(param=param),
2025+
self.assertRaisesMessage(TypeError, msg),
2026+
):
2027+
mail.send_mail(
2028+
"subject",
2029+
"body",
2030+
"from@example.com",
2031+
["to@example.com"],
2032+
**{param: "value"},
2033+
connection=mail.get_connection(),
2034+
)
2035+
2036+
def test_email_message_send_fail_silently_conflict(self):
2037+
email = mail.EmailMessage(
2038+
"Subject",
2039+
"Body",
2040+
"from@example.com",
2041+
["to@example.com"],
2042+
connection=mail.get_connection(),
2043+
)
2044+
msg = (
2045+
"fail_silently cannot be used with a connection. "
2046+
"Pass fail_silently to get_connection() instead."
2047+
)
2048+
with self.assertRaisesMessage(TypeError, msg):
2049+
email.send(fail_silently=True)
2050+
2051+
def test_send_mass_mail_fail_silently_conflict(self):
2052+
datatuple = (("Subject", "Message", "from@example.com", ["to@example.com"]),)
2053+
msg = (
2054+
"fail_silently cannot be used with a connection. "
2055+
"Pass fail_silently to get_connection() instead."
2056+
)
2057+
with self.assertRaisesMessage(TypeError, msg):
2058+
mail.send_mass_mail(
2059+
datatuple, fail_silently=True, connection=mail.get_connection()
2060+
)
2061+
2062+
def test_send_mass_mail_auth_conflict(self):
2063+
datatuple = (("Subject", "Message", "from@example.com", ["to@example.com"]),)
2064+
msg = (
2065+
"auth_user and auth_password cannot be used with a connection. "
2066+
"Pass auth_user and auth_password to get_connection() instead."
2067+
)
2068+
for param in ["auth_user", "auth_password"]:
2069+
with (
2070+
self.subTest(param=param),
2071+
self.assertRaisesMessage(TypeError, msg),
2072+
):
2073+
mail.send_mass_mail(
2074+
datatuple, **{param: "value"}, connection=mail.get_connection()
2075+
)
2076+
2077+
def test_mail_admins_fail_silently_conflict(self):
2078+
msg = (
2079+
"fail_silently cannot be used with a connection. "
2080+
"Pass fail_silently to get_connection() instead."
2081+
)
2082+
with self.assertRaisesMessage(TypeError, msg):
2083+
mail.mail_admins(
2084+
"Subject",
2085+
"Message",
2086+
fail_silently=True,
2087+
connection=mail.get_connection(),
2088+
)
2089+
2090+
def test_mail_managers_fail_silently_conflict(self):
2091+
msg = (
2092+
"fail_silently cannot be used with a connection. "
2093+
"Pass fail_silently to get_connection() instead."
2094+
)
2095+
with self.assertRaisesMessage(TypeError, msg):
2096+
mail.mail_managers(
2097+
"Subject",
2098+
"Message",
2099+
fail_silently=True,
2100+
connection=mail.get_connection(),
2101+
)
2102+
20022103

20032104
# RemovedInDjango70Warning.
20042105
class MailDeprecatedPositionalArgsTests(SimpleTestCase):
@@ -2029,9 +2130,9 @@ def test_send_mail(self):
20292130
"from@example.com",
20302131
["to@example.com"],
20312132
# Deprecated positional args:
2032-
True,
2033-
"username",
2034-
"password",
2133+
None,
2134+
None,
2135+
None,
20352136
mail.get_connection(),
20362137
"html message",
20372138
)
@@ -2044,9 +2145,9 @@ def test_send_mass_mail(self):
20442145
send_mass_mail(
20452146
[],
20462147
# Deprecated positional args:
2047-
True,
2048-
"username",
2049-
"password",
2148+
None,
2149+
None,
2150+
None,
20502151
mail.get_connection(),
20512152
)
20522153

@@ -2058,7 +2159,7 @@ def test_mail_admins(self):
20582159
"subject",
20592160
"message",
20602161
# Deprecated positional args:
2061-
True,
2162+
None,
20622163
mail.get_connection(),
20632164
"html message",
20642165
)
@@ -2071,7 +2172,7 @@ def test_mail_managers(self):
20712172
"subject",
20722173
"message",
20732174
# Deprecated positional args:
2074-
True,
2175+
None,
20752176
mail.get_connection(),
20762177
"html message",
20772178
)

0 commit comments

Comments
 (0)