Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #21271 -- Added timeout parameter to SMTP EmailBackend.

Thanks Tobias McNulty and Tim Graham for discussions and code review.
Thanks Andre Cruz the suggestion and initial patch.
  • Loading branch information...
commit 4e0a2fe59c8b9c32c2f3111474354356474128a8 1 parent 9eecb91
@onceuponatimeforever onceuponatimeforever authored timgraham committed
View
36 django/core/mail/backends/smtp.py
@@ -15,7 +15,8 @@ class EmailBackend(BaseEmailBackend):
A wrapper that manages the SMTP network connection.
"""
def __init__(self, host=None, port=None, username=None, password=None,
- use_tls=None, fail_silently=False, use_ssl=None, **kwargs):
+ use_tls=None, fail_silently=False, use_ssl=None, timeout=None,
+ **kwargs):
super(EmailBackend, self).__init__(fail_silently=fail_silently)
self.host = host or settings.EMAIL_HOST
self.port = port or settings.EMAIL_PORT
@@ -23,6 +24,7 @@ def __init__(self, host=None, port=None, username=None, password=None,
self.password = settings.EMAIL_HOST_PASSWORD if password is None else password
self.use_tls = settings.EMAIL_USE_TLS if use_tls is None else use_tls
self.use_ssl = settings.EMAIL_USE_SSL if use_ssl is None else use_ssl
+ self.timeout = timeout
if self.use_ssl and self.use_tls:
raise ValueError(
"EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set "
@@ -38,24 +40,22 @@ def open(self):
if self.connection:
# Nothing to do if the connection is already open.
return False
+
+ connection_class = smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP
+ # If local_hostname is not specified, socket.getfqdn() gets used.
+ # For performance, we use the cached FQDN for local_hostname.
+ connection_params = {'local_hostname': DNS_NAME.get_fqdn()}
+ if self.timeout is not None:
+ connection_params['timeout'] = self.timeout
try:
- # If local_hostname is not specified, socket.getfqdn() gets used.
- # For performance, we use the cached FQDN for local_hostname.
- if self.use_ssl:
- self.connection = smtplib.SMTP_SSL(self.host, self.port,
- local_hostname=DNS_NAME.get_fqdn())
- else:
- self.connection = smtplib.SMTP(self.host, self.port,
- local_hostname=DNS_NAME.get_fqdn())
- # TLS/SSL are mutually exclusive, so only attempt TLS over
- # non-secure connections.
- if self.use_tls:
- self.connection.ehlo()
- self.connection.starttls()
- self.connection.ehlo()
- if self.username and self.password:
- self.connection.login(self.username, self.password)
@claudep Collaborator
claudep added a note

The login() call has been inadvertantly deleted. I'll fix that and add a test for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
- return True
@mjtamlyn Collaborator

This line was also inadvertently deleted, and is exceedingly important as without it connections are never closed. Readded and tested in 9d2c5b0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ self.connection = connection_class(self.host, self.port, **connection_params)
+
+ # TLS/SSL are mutually exclusive, so only attempt TLS over
+ # non-secure connections.
+ if not self.use_ssl and self.use_tls:
+ self.connection.ehlo()
+ self.connection.starttls()
+ self.connection.ehlo()
except smtplib.SMTPException:
if not self.fail_silently:
raise
View
2  docs/releases/1.7.txt
@@ -248,6 +248,8 @@ Email
* :func:`~django.core.mail.send_mail` now accepts an ``html_message``
parameter for sending a multipart ``text/plain`` and ``text/html`` email.
+* The SMTP :class:`~django.core.mail.backends.smtp.EmailBackend` now accepts a
+ :attr:`~django.core.mail.backends.smtp.EmailBackend.timeout` parameter.
File Uploads
^^^^^^^^^^^^
View
38 docs/topics/email.txt
@@ -424,16 +424,38 @@ can :ref:`write your own email backend <topic-custom-email-backend>`.
SMTP backend
~~~~~~~~~~~~
-This is the default backend. Email will be sent through a SMTP server.
-The server address and authentication credentials are set in the
-:setting:`EMAIL_HOST`, :setting:`EMAIL_PORT`, :setting:`EMAIL_HOST_USER`,
-:setting:`EMAIL_HOST_PASSWORD`, :setting:`EMAIL_USE_TLS` and
-:setting:`EMAIL_USE_SSL` settings in your settings file.
+.. class:: backends.smtp.EmailBackend([host=None, port=None, username=None, password=None, use_tls=None, fail_silently=False, use_ssl=None, timeout=None, **kwargs])
-The SMTP backend is the default configuration inherited by Django. If you
-want to specify it explicitly, put the following in your settings::
+ This is the default backend. Email will be sent through a SMTP server.
+ The server address and authentication credentials are set in the
+ :setting:`EMAIL_HOST`, :setting:`EMAIL_PORT`, :setting:`EMAIL_HOST_USER`,
+ :setting:`EMAIL_HOST_PASSWORD`, :setting:`EMAIL_USE_TLS` and
+ :setting:`EMAIL_USE_SSL` settings in your settings file.
- EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+ The SMTP backend is the default configuration inherited by Django. If you
+ want to specify it explicitly, put the following in your settings::
+
+ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+
+ Here is an attribute which doesn't have a corresponding settting like the

Typo in settting.

@timgraham Owner

Fixed in 43cfc65, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ others described above:
+
+ .. attribute:: timeout
+
+ .. versionadded:: 1.7
+
+ This backend contains a ``timeout`` parameter, which can be set with
+ the following sample code::
+
+ from django.core.mail.backends import smtp
+
+ class MyEmailBackend(smtp.EmailBackend):
+ def __init__(self, *args, **kwargs):
+ kwargs.setdefault('timeout', 42)
+ super(MyEmailBackend, self).__init__(*args, **kwargs)
+
+ Then point the :setting:`EMAIL_BACKEND` setting at your custom backend as
+ described above.
.. _topic-email-console-backend:
View
17 tests/mail/tests.py
@@ -933,3 +933,20 @@ def test_email_ssl_attempts_ssl_connection(self):
backend = smtp.EmailBackend()
self.assertTrue(backend.use_ssl)
self.assertRaises(SSLError, backend.open)
+
+ def test_connection_timeout_default(self):
+ """Test that the connection's timeout value is None by default."""
+ connection = mail.get_connection('django.core.mail.backends.smtp.EmailBackend')
+ self.assertEqual(connection.timeout, None)
+
+ def test_connection_timeout_custom(self):
+ """Test that the timeout parameter can be customized."""
+ class MyEmailBackend(smtp.EmailBackend):
+ def __init__(self, *args, **kwargs):
+ kwargs.setdefault('timeout', 42)
+ super(MyEmailBackend, self).__init__(*args, **kwargs)
+
+ myemailbackend = MyEmailBackend()
+ myemailbackend.open()
+ self.assertEqual(myemailbackend.timeout, 42)
+ self.assertEqual(myemailbackend.connection.timeout, 42)
Please sign in to comment.
Something went wrong with that request. Please try again.