Просмотр исходного кода

Merge pull request #28 from Miksus/dev/connect

add: support for sending multiple emails
Mikael Koli 4 лет назад
Родитель
Сommit
693ca593fe
3 измененных файлов с 104 добавлено и 8 удалено
  1. 47 1
      docs/tutorials/sending.rst
  2. 34 6
      redmail/email/sender.py
  3. 23 1
      redmail/test/email/test_send.py

+ 47 - 1
docs/tutorials/sending.rst

@@ -116,4 +116,50 @@ You can also alias the sender and receivers:
 
 
 Alias is an alternative text that is displayed instead of 
 Alias is an alternative text that is displayed instead of 
 the actual email addresses. The receivers can still get 
 the actual email addresses. The receivers can still get 
-the addresses though.
+the addresses though.
+
+.. _send-multi:
+
+Sending Multiple Emails
+-----------------------
+
+Normally Red Mail opens and closes the connection to the SMTP
+server when sending each email. If you are sending large amount
+of emails it may be beneficial to leave the connection open:
+
+.. code-block:: python
+
+    with email:
+        email.send(
+            subject='email subject',
+            sender="me@example.com",
+            receivers=['you@example.com']
+        )
+
+        email.send(
+            subject='email subject',
+            sender="me@example.com",
+            receivers=['they@example.com']
+        )
+        ...
+
+Alternatively, you may use the ``connect`` and ``close``
+methods:
+
+.. code-block:: python
+
+    try:
+        email.connect()
+        email.send(
+            subject='email subject',
+            sender="me@example.com",
+            receivers=['you@example.com']
+        )
+        email.send(
+            subject='email subject',
+            sender="me@example.com",
+            receivers=['they@example.com']
+        )
+        ...
+    finally:
+        email.close()

+ 34 - 6
redmail/email/sender.py

@@ -97,6 +97,10 @@ class EmailSender:
     kws_smtp : dict
     kws_smtp : dict
         Keyword arguments passed to ``cls_smtp``
         Keyword arguments passed to ``cls_smtp``
         when connecting to the SMTP server.
         when connecting to the SMTP server.
+    connection : smtplib.SMTP, None
+        Connection to the SMTP server. Created and closed
+        before and after sending each email unless there 
+        is an existing connection.
 
 
     Examples
     Examples
     --------
     --------
@@ -151,6 +155,8 @@ class EmailSender:
         self.cls_smtp = cls_smtp
         self.cls_smtp = cls_smtp
         self.kws_smtp = kwargs
         self.kws_smtp = kwargs
         
         
+        self.connection = None
+
     def send(self,
     def send(self,
              subject:Optional[str]=None,
              subject:Optional[str]=None,
              sender:Optional[str]=None,
              sender:Optional[str]=None,
@@ -369,14 +375,31 @@ class EmailSender:
 
 
     def send_message(self, msg:EmailMessage):
     def send_message(self, msg:EmailMessage):
         "Send the created message"
         "Send the created message"
-
-        server = self.connect()
-        server.send_message(msg)
-        
-        server.quit()
+        if self.is_alive:
+            self.connection.send_message(msg)
+        else:
+            # The connection was opened for this message
+            # thus it is also closed with this message
+            with self:
+                self.connection.send_message(msg)
     
     
-    def connect(self) -> smtplib.SMTP:
+    def __enter__(self):
+        self.connect()
+
+    def __exit__(self, *args):
+        self.close()
+
+    def connect(self):
         "Connect to the SMTP Server"
         "Connect to the SMTP Server"
+        self.connection = self.get_server()
+
+    def close(self):
+        "Close (quit) the connection"
+        self.connection.quit()
+        self.connection = None
+
+    def get_server(self) -> smtplib.SMTP:
+        "Connect and get the SMTP Server"
         user = self.user_name
         user = self.user_name
         password = self.password
         password = self.password
         
         
@@ -388,6 +411,11 @@ class EmailSender:
             server.login(user, password)
             server.login(user, password)
         return server
         return server
 
 
+    @property
+    def is_alive(self):
+        "bool: Check if there is a connection to the SMTP server"
+        return self.connection is not None
+
     def get_params(self, sender:str) -> Dict[str, Any]:
     def get_params(self, sender:str) -> Dict[str, Any]:
         "Get Jinja parametes passed to both text and html bodies"
         "Get Jinja parametes passed to both text and html bodies"
         # TODO: Add receivers to params
         # TODO: Add receivers to params

+ 23 - 1
redmail/test/email/test_send.py

@@ -26,12 +26,34 @@ class MockServer:
 
 
 def test_send():
 def test_send():
     email = EmailSender(host="localhost", port=0, cls_smtp=MockServer)
     email = EmailSender(host="localhost", port=0, cls_smtp=MockServer)
-    # This should fail but we test everything else goes through
+    assert email.connection is None
+
     msg = email.send(
     msg = email.send(
         subject="An example",
         subject="An example",
         receivers=['koli.mikael@example.com']
         receivers=['koli.mikael@example.com']
     )
     )
     assert isinstance(msg, EmailMessage)
     assert isinstance(msg, EmailMessage)
+    assert email.connection is None
+
+def test_send_multi():
+    email = EmailSender(host="localhost", port=0, cls_smtp=MockServer)
+
+    assert email.connection is None
+    with email:
+        assert email.connection is not None
+        msg = email.send(
+            subject="An example",
+            receivers=['koli.mikael@example.com']
+        )
+        assert isinstance(msg, EmailMessage)
+        assert email.connection is not None
+        msg = email.send(
+            subject="An example",
+            receivers=['koli.mikael@example.com']
+        )
+        assert isinstance(msg, EmailMessage)
+        assert email.connection is not None
+    assert email.connection is None
 
 
 def test_send_function():
 def test_send_function():
     # This should fail but we test everything else goes through
     # This should fail but we test everything else goes through