Explorar o código

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.
SusanTan %!s(int64=11) %!d(string=hai) anos
pai
achega
4e0a2fe59c
Modificáronse 4 ficheiros con 67 adicións e 26 borrados
  1. 18 18
      django/core/mail/backends/smtp.py
  2. 2 0
      docs/releases/1.7.txt
  3. 30 8
      docs/topics/email.txt
  4. 17 0
      tests/mail/tests.py

+ 18 - 18
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 @@ class EmailBackend(BaseEmailBackend):
         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 @@ class EmailBackend(BaseEmailBackend):
         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)
-            return True
+            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

+ 2 - 0
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
 ^^^^^^^^^^^^

+ 30 - 8
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
+    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:
 

+ 17 - 0
tests/mail/tests.py

@@ -933,3 +933,20 @@ class SMTPBackendTests(BaseEmailBackendTests, SimpleTestCase):
         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)