Matthew Frazier committed 79a2f57

altered the sender interface

Comments (0)

Files changed (6)


     def send(self, messages):
-        This sends the given messages and returns the number of messages sent.
-        It should automatically call the `use` and `done` methods.
+        This sends the given messages. It should be able to accept any kind of
+        iterable, as well as a lone `Message` instance.
+        It should automatically call the `use` and `done` methods. (You can
+        use `send_wrapper` to make this a bit easier.)
+        Due to the many perils and uncertainties of sending e-mail, the `send`
+        method takes a very optimistic approach to exceptions. It returns a
+        `dict` that maps `Message` objects to exceptions. If an error
+        happens during the delivery of a message that results in the message
+        definitely not being sent to anyone, the error is stored in the `dict`
+        with the associated `Message` as the key. Any other failures that it
+        is capable of detecting should merely be logged.
+        Exceptions should only be **raised** for connection-level errors (i.e.
+        the SMTP server refused the given credentials).
         raise NotImplementedError("This doesn't actually send messages!")
     You can wrap your `send` method with this to make your life easier. When
     called, this will automatically:
-    - Return with 0 if an empty iterable was given
+    - Return with {} if an empty iterable was given
     - Ensure that the messages are a `tuple` (even if only a single one was
       passed in) and that they are in fact `Message` instances
     - Call `~Sender.use` and `~Sender.done` for you
             messages = tuple(messages)
         if not messages:
-            return 0
+            return {}
         if not all(isinstance(m, Message) for m in messages):
             raise TypeError("Only Message instances can be passed to send")


-        return len(messages)
+        return {}
     def format(self, msg):
         if self.config.format == 'friendly':


                          `Message` instance).
-        return len(messages)
+        return {}


             for rcpt in msg.recipients:
       'NullSender: sending message %s to %s' %
                                (id(msg), rcpt))
-        return len(messages)
+        return {}
 sender_class = NullSender


     from StringIO import StringIO
-class StreamTrapper(object):
-    "Hack of epic proportions."
-    def trap_stdout(self):
-        self.old_stdout = sys.stdout
-        sys.stdout = StringIO()
-    def untrap_stdout(self):
-        buf = sys.stdout
-        sys.stdout = self.old_stdout
-        return buf.getvalue()
-    def trap_stderr(self):
-        self.old_stderr = sys.stderr
-        sys.stderr = StringIO()
-    def untrap_stderr(self):
-        buf = sys.stderr
-        sys.stderr = self.old_stderr
-        return buf.getvalue()
 def make_test_message():
     text = (u'Get to Coruscant as soon as possible, and don\'t tell '
             u'anyone, even General Cracken.')


     def test_send_wrapper(self):
         sdr = Dummy()
-        assert sdr.send(()) == 0
+        assert sdr.send(()) == {}
         msg1 = Message(to='tycho.celchu@starfighter.gffa')
         assert sdr.send(msg1) == (msg1,)
         assert sdr.send([msg1]) == (msg1,)