From 4aa80149e71d02947164a89ec5da6fd035393425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sun, 23 Mar 2014 17:29:10 +0100 Subject: [PATCH] Fixed #22327 -- Turned BaseEmailBackend into a context manager Changed the BaseEmailBackend to allow usage as context manager to open and close connections. --- django/core/mail/backends/base.py | 14 ++++++++++++++ docs/releases/1.8.txt | 3 ++- docs/topics/email.txt | 15 +++++++++++++++ tests/mail/tests.py | 21 +++++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/django/core/mail/backends/base.py b/django/core/mail/backends/base.py index 38f0523191..484d2a8b57 100644 --- a/django/core/mail/backends/base.py +++ b/django/core/mail/backends/base.py @@ -6,6 +6,13 @@ class BaseEmailBackend(object): Base class for email backend implementations. Subclasses must at least overwrite send_messages(). + + open() and close() can be called indirectly by using a backend object as a + context manager: + + with backend as connection: + # do something with connection + pass """ def __init__(self, fail_silently=False, **kwargs): self.fail_silently = fail_silently @@ -32,6 +39,13 @@ class BaseEmailBackend(object): """Close a network connection.""" pass + def __enter__(self): + self.open() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + def send_messages(self, email_messages): """ Sends one or more EmailMessage objects and returns the number of email diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 789c1db535..d86836a50f 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -91,7 +91,8 @@ Cache Email ^^^^^ -* ... +* :ref:`Email backends ` now support the context manager + protocol for opening and closing connections. File Storage ^^^^^^^^^^^^ diff --git a/docs/topics/email.txt b/docs/topics/email.txt index 6b93d7d2b8..1989b5e4f3 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -393,6 +393,21 @@ The email backend class has the following methods: connection afterwards. If the connection is already open, it will be left open after mail has been sent. +It can also be used as a context manager, which will automatically call +``open()`` and ``close()`` as needed:: + + from django.core import mail + + with mail.get_connection() as connection: + mail.EmailMessage(subject1, body1, from1, [to1], + connection=connection).send() + mail.EmailMessage(subject2, body2, from2, [to2], + connection=connection).send() + +.. versionadded:: 1.8 + + The context manager protocol was added. + Obtaining an instance of an email backend ----------------------------------------- diff --git a/tests/mail/tests.py b/tests/mail/tests.py index 06292b3cb6..37b18704c7 100644 --- a/tests/mail/tests.py +++ b/tests/mail/tests.py @@ -638,6 +638,27 @@ class BaseEmailBackendTests(HeadersCheckMixin, object): except Exception as e: self.fail("close() unexpectedly raised an exception: %s" % e) + def test_use_as_contextmanager(self): + """ + Test that the connection can be used as a contextmanager. + """ + opened = [False] + closed = [False] + conn = mail.get_connection(username='', password='') + + def open(): + opened[0] = True + conn.open = open + + def close(): + closed[0] = True + conn.close = close + with conn as same_conn: + self.assertTrue(opened[0]) + self.assertIs(same_conn, conn) + self.assertFalse(closed[0]) + self.assertTrue(closed[0]) + class LocmemBackendTests(BaseEmailBackendTests, SimpleTestCase): email_backend = 'django.core.mail.backends.locmem.EmailBackend'