[Checkins] SVN: zope.sendmail/trunk/src/zope/sendmail/ Moved the QueueProcessorThread from zope.sendmail.delivery to its own module

Fabio Tranchitella kobold at kobold.it
Thu Jan 7 02:11:43 EST 2010


Log message for revision 107770:
  Moved the QueueProcessorThread from zope.sendmail.delivery to its own module
  zope.sendmail.queue; moved the QueueProcessorThread unit tests from
  zope.sendmail.tests.test_delivery to their own module test_queue.
  
  
  

Changed:
  U   zope.sendmail/trunk/src/zope/sendmail/delivery.py
  A   zope.sendmail/trunk/src/zope/sendmail/queue.py
  U   zope.sendmail/trunk/src/zope/sendmail/tests/test_delivery.py
  U   zope.sendmail/trunk/src/zope/sendmail/tests/test_directives.py
  A   zope.sendmail/trunk/src/zope/sendmail/tests/test_queue.py
  U   zope.sendmail/trunk/src/zope/sendmail/zcml.py

-=-
Modified: zope.sendmail/trunk/src/zope/sendmail/delivery.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/delivery.py	2010-01-07 06:31:54 UTC (rev 107769)
+++ zope.sendmail/trunk/src/zope/sendmail/delivery.py	2010-01-07 07:11:43 UTC (rev 107770)
@@ -19,15 +19,8 @@
 """
 __docformat__ = 'restructuredtext'
 
-import atexit
-import logging
 import os
-import os.path
 import rfc822
-import smtplib
-import stat
-import threading
-import time
 from cStringIO import StringIO
 from random import randrange
 from time import strftime
@@ -39,19 +32,7 @@
 from transaction.interfaces import IDataManager
 import transaction
 
-import sys
-if sys.platform == 'win32':
-    import win32file
-    _os_link = lambda src, dst: win32file.CreateHardLink(dst, src, None)
-else:
-    _os_link = os.link
 
-# The longest time sending a file is expected to take.  Longer than this and
-# the send attempt will be assumed to have failed.  This means that sending
-# very large files or using very slow mail servers could result in duplicate
-# messages sent.
-MAX_SEND_TIME = 60*60*3
-
 class MailDataManager(object):
     implements(IDataManager)
 
@@ -151,280 +132,3 @@
         msg.write(message)
         msg.close()
         return MailDataManager(msg.commit, onAbort=msg.abort)
-
-
-# The below diagram depicts the operations performed while sending a message in
-# the ``run`` method of ``QueueProcessorThread``.  This sequence of operations
-# will be performed for each file in the maildir each time the thread "wakes
-# up" to send messages.
-#
-# Any error conditions not depected on the diagram will provoke the catch-all
-# exception logging of the ``run`` method.
-#
-# In the diagram the "message file" is the file in the maildir's "cur" directory
-# that contains the message and "tmp file" is a hard link to the message file
-# created in the maildir's "tmp" directory.
-#
-#           ( start trying to deliver a message )
-#                            |
-#                            |
-#                            V
-#            +-----( get tmp file mtime )
-#            |               |
-#            |               | file exists
-#            |               V
-#            |         ( check age )-----------------------------+
-#   tmp file |               |                       file is new |
-#   does not |               | file is old                       |
-#   exist    |               |                                   |
-#            |      ( unlink tmp file )-----------------------+  |
-#            |               |                      file does |  |
-#            |               | file unlinked        not exist |  |
-#            |               V                                |  |
-#            +---->( touch message file )------------------+  |  |
-#                            |                   file does |  |  |
-#                            |                   not exist |  |  |
-#                            V                             |  |  |
-#            ( link message file to tmp file )----------+  |  |  |
-#                            |                 tmp file |  |  |  |
-#                            |           already exists |  |  |  |
-#                            |                          |  |  |  |
-#                            V                          V  V  V  V
-#                     ( send message )             ( skip this message )
-#                            |
-#                            V
-#                 ( unlink message file )---------+
-#                            |                    |
-#                            | file unlinked      | file no longer exists
-#                            |                    |
-#                            |  +-----------------+
-#                            |  |
-#                            |  V
-#                  ( unlink tmp file )------------+
-#                            |                    |
-#                            | file unlinked      | file no longer exists
-#                            V                    |
-#                  ( message delivered )<---------+
-
-class QueueProcessorThread(threading.Thread):
-    """This thread is started at configuration time from the
-    `mail:queuedDelivery` directive handler.
-    """
-
-    log = logging.getLogger("QueueProcessorThread")
-    _stopped = False
-    interval = 3.0   # process queue every X second
-
-    def __init__(self, interval=3.0):
-        threading.Thread.__init__(self)
-        self.interval = interval
-        self._lock = threading.Lock()
-        self.setDaemon(True)
-
-    def setMaildir(self, maildir):
-        """Set the maildir.
-
-        This method is used just to provide a `maildir` stubs ."""
-        self.maildir = maildir
-
-    def setQueuePath(self, path):
-        self.maildir = Maildir(path, True)
-
-    def setMailer(self, mailer):
-        self.mailer = mailer
-
-    def _parseMessage(self, message):
-        """Extract fromaddr and toaddrs from the first two lines of
-        the `message`.
-
-        Returns a fromaddr string, a toaddrs tuple and the message
-        string.
-        """
-
-        fromaddr = ""
-        toaddrs = ()
-        rest = ""
-
-        try:
-            first, second, rest = message.split('\n', 2)
-        except ValueError:
-            return fromaddr, toaddrs, message
-
-        if first.startswith("X-Zope-From: "):
-            i = len("X-Zope-From: ")
-            fromaddr = first[i:]
-
-        if second.startswith("X-Zope-To: "):
-            i = len("X-Zope-To: ")
-            toaddrs = tuple(second[i:].split(", "))
-
-        return fromaddr, toaddrs, rest
-
-    def run(self, forever=True):
-        atexit.register(self.stop)
-        while not self._stopped:
-            for filename in self.maildir:
-                # if we are asked to stop while sending messages, do so
-                if self._stopped:
-                    break
-
-                fromaddr = ''
-                toaddrs = ()
-                head, tail = os.path.split(filename)
-                tmp_filename = os.path.join(head, '.sending-' + tail)
-                rejected_filename = os.path.join(head, '.rejected-' + tail)
-                try:
-                    # perform a series of operations in an attempt to ensure
-                    # that no two threads/processes send this message
-                    # simultaneously as well as attempting to not generate
-                    # spurious failure messages in the log; a diagram that
-                    # represents these operations is included in a
-                    # comment above this class
-                    try:
-                        # find the age of the tmp file (if it exists)
-                        age = None
-                        mtime = os.stat(tmp_filename)[stat.ST_MTIME]
-                        age = time.time() - mtime
-                    except OSError, e:
-                        if e.errno == 2: # file does not exist
-                            # the tmp file could not be stated because it
-                            # doesn't exist, that's fine, keep going
-                            pass
-                        else:
-                            # the tmp file could not be stated for some reason
-                            # other than not existing; we'll report the error
-                            raise
-
-                    # if the tmp file exists, check it's age
-                    if age is not None:
-                        try:
-                            if age > MAX_SEND_TIME:
-                                # the tmp file is "too old"; this suggests
-                                # that during an attemt to send it, the
-                                # process died; remove the tmp file so we
-                                # can try again
-                                os.unlink(tmp_filename)
-                            else:
-                                # the tmp file is "new", so someone else may
-                                # be sending this message, try again later
-                                continue
-                            # if we get here, the file existed, but was too
-                            # old, so it was unlinked
-                        except OSError, e:
-                            if e.errno == 2: # file does not exist
-                                # it looks like someone else removed the tmp
-                                # file, that's fine, we'll try to deliver the
-                                # message again later
-                                continue
-
-                    # now we know that the tmp file doesn't exist, we need to
-                    # "touch" the message before we create the tmp file so the
-                    # mtime will reflect the fact that the file is being
-                    # processed (there is a race here, but it's OK for two or
-                    # more processes to touch the file "simultaneously")
-                    try:
-                        os.utime(filename, None)
-                    except OSError, e:
-                        if e.errno == 2: # file does not exist
-                            # someone removed the message before we could
-                            # touch it, no need to complain, we'll just keep
-                            # going
-                            continue
-
-                    # creating this hard link will fail if another process is
-                    # also sending this message
-                    try:
-                        #os.link(filename, tmp_filename)
-                        _os_link(filename, tmp_filename)
-                    except OSError, e:
-                        if e.errno == 17: # file exists, *nix
-                            # it looks like someone else is sending this
-                            # message too; we'll try again later
-                            continue
-                    except error, e:
-                        if e[0] == 183 and e[1] == 'CreateHardLink':
-                            # file exists, win32
-                            continue
-
-                    # read message file and send contents
-                    file = open(filename)
-                    message = file.read()
-                    file.close()
-                    fromaddr, toaddrs, message = self._parseMessage(message)
-                    # The next block is the only one that is sensitive to
-                    # interruptions.  Everywhere else, if this daemon thread
-                    # stops, we should be able to correctly handle a restart.
-                    # In this block, if we send the message, but we are
-                    # stopped before we unlink the file, we will resend the
-                    # message when we are restarted.  We limit the likelihood
-                    # of this somewhat by using a lock to link the two
-                    # operations.  When the process gets an interrupt, it
-                    # will call the atexit that we registered (``stop``
-                    # below).  This will try to get the same lock before it
-                    # lets go.  Because this can cause the daemon thread to
-                    # continue (that is, to not act like a daemon thread), we
-                    # still use the _stopped flag to communicate.
-                    self._lock.acquire()
-                    try:
-                        try:
-                            self.mailer.send(fromaddr, toaddrs, message)
-                        except smtplib.SMTPResponseException, e:
-                            if 500 <= e.smtp_code <= 599:
-                                # permanent error, ditch the message
-                                self.log.error(
-                                    "Discarding email from %s to %s due to"
-                                    " a permanent error: %s",
-                                    fromaddr, ", ".join(toaddrs), str(e))
-                                #os.link(filename, rejected_filename)
-                                _os_link(filename, rejected_filename)
-                            else:
-                                # Log an error and retry later
-                                raise
-
-                        try:
-                            os.unlink(filename)
-                        except OSError, e:
-                            if e.errno == 2: # file does not exist
-                                # someone else unlinked the file; oh well
-                                pass
-                            else:
-                                # something bad happend, log it
-                                raise
-                    finally:
-                        self._lock.release()
-                    try:
-                        os.unlink(tmp_filename)
-                    except OSError, e:
-                        if e.errno == 2: # file does not exist
-                            # someone else unlinked the file; oh well
-                            pass
-                        else:
-                            # something bad happend, log it
-                            raise
-
-                    # TODO: maybe log the Message-Id of the message sent
-                    self.log.info("Mail from %s to %s sent.",
-                                  fromaddr, ", ".join(toaddrs))
-                    # Blanket except because we don't want
-                    # this thread to ever die
-                except:
-                    if fromaddr != '' or toaddrs != ():
-                        self.log.error(
-                            "Error while sending mail from %s to %s.",
-                            fromaddr, ", ".join(toaddrs), exc_info=True)
-                    else:
-                        self.log.error(
-                            "Error while sending mail : %s ",
-                            filename, exc_info=True)
-            else:
-                if forever:
-                    time.sleep(self.interval)
-
-            # A testing plug
-            if not forever:
-                break
-
-    def stop(self):
-        self._stopped = True
-        self._lock.acquire()
-        self._lock.release()

Copied: zope.sendmail/trunk/src/zope/sendmail/queue.py (from rev 106770, zope.sendmail/trunk/src/zope/sendmail/delivery.py)
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/queue.py	                        (rev 0)
+++ zope.sendmail/trunk/src/zope/sendmail/queue.py	2010-01-07 07:11:43 UTC (rev 107770)
@@ -0,0 +1,319 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Mail Delivery utility implementation
+
+This module contains various implementations of `MailDeliverys`.
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+import atexit
+import logging
+import os
+import smtplib
+import stat
+import threading
+import time
+
+from zope.sendmail.maildir import Maildir
+
+import sys
+if sys.platform == 'win32':
+    import win32file
+    _os_link = lambda src, dst: win32file.CreateHardLink(dst, src, None)
+else:
+    _os_link = os.link
+
+# The longest time sending a file is expected to take.  Longer than this and
+# the send attempt will be assumed to have failed.  This means that sending
+# very large files or using very slow mail servers could result in duplicate
+# messages sent.
+MAX_SEND_TIME = 60*60*3
+
+# The below diagram depicts the operations performed while sending a message in
+# the ``run`` method of ``QueueProcessorThread``.  This sequence of operations
+# will be performed for each file in the maildir each time the thread "wakes
+# up" to send messages.
+#
+# Any error conditions not depected on the diagram will provoke the catch-all
+# exception logging of the ``run`` method.
+#
+# In the diagram the "message file" is the file in the maildir's "cur" directory
+# that contains the message and "tmp file" is a hard link to the message file
+# created in the maildir's "tmp" directory.
+#
+#           ( start trying to deliver a message )
+#                            |
+#                            |
+#                            V
+#            +-----( get tmp file mtime )
+#            |               |
+#            |               | file exists
+#            |               V
+#            |         ( check age )-----------------------------+
+#   tmp file |               |                       file is new |
+#   does not |               | file is old                       |
+#   exist    |               |                                   |
+#            |      ( unlink tmp file )-----------------------+  |
+#            |               |                      file does |  |
+#            |               | file unlinked        not exist |  |
+#            |               V                                |  |
+#            +---->( touch message file )------------------+  |  |
+#                            |                   file does |  |  |
+#                            |                   not exist |  |  |
+#                            V                             |  |  |
+#            ( link message file to tmp file )----------+  |  |  |
+#                            |                 tmp file |  |  |  |
+#                            |           already exists |  |  |  |
+#                            |                          |  |  |  |
+#                            V                          V  V  V  V
+#                     ( send message )             ( skip this message )
+#                            |
+#                            V
+#                 ( unlink message file )---------+
+#                            |                    |
+#                            | file unlinked      | file no longer exists
+#                            |                    |
+#                            |  +-----------------+
+#                            |  |
+#                            |  V
+#                  ( unlink tmp file )------------+
+#                            |                    |
+#                            | file unlinked      | file no longer exists
+#                            V                    |
+#                  ( message delivered )<---------+
+
+class QueueProcessorThread(threading.Thread):
+    """This thread is started at configuration time from the
+    `mail:queuedDelivery` directive handler.
+    """
+
+    log = logging.getLogger("QueueProcessorThread")
+    _stopped = False
+    interval = 3.0   # process queue every X second
+
+    def __init__(self, interval=3.0):
+        threading.Thread.__init__(self)
+        self.interval = interval
+        self._lock = threading.Lock()
+        self.setDaemon(True)
+
+    def setMaildir(self, maildir):
+        """Set the maildir.
+
+        This method is used just to provide a `maildir` stubs ."""
+        self.maildir = maildir
+
+    def setQueuePath(self, path):
+        self.maildir = Maildir(path, True)
+
+    def setMailer(self, mailer):
+        self.mailer = mailer
+
+    def _parseMessage(self, message):
+        """Extract fromaddr and toaddrs from the first two lines of
+        the `message`.
+
+        Returns a fromaddr string, a toaddrs tuple and the message
+        string.
+        """
+
+        fromaddr = ""
+        toaddrs = ()
+        rest = ""
+
+        try:
+            first, second, rest = message.split('\n', 2)
+        except ValueError:
+            return fromaddr, toaddrs, message
+
+        if first.startswith("X-Zope-From: "):
+            i = len("X-Zope-From: ")
+            fromaddr = first[i:]
+
+        if second.startswith("X-Zope-To: "):
+            i = len("X-Zope-To: ")
+            toaddrs = tuple(second[i:].split(", "))
+
+        return fromaddr, toaddrs, rest
+
+    def run(self, forever=True):
+        atexit.register(self.stop)
+        while not self._stopped:
+            for filename in self.maildir:
+                # if we are asked to stop while sending messages, do so
+                if self._stopped:
+                    break
+
+                fromaddr = ''
+                toaddrs = ()
+                head, tail = os.path.split(filename)
+                tmp_filename = os.path.join(head, '.sending-' + tail)
+                rejected_filename = os.path.join(head, '.rejected-' + tail)
+                try:
+                    # perform a series of operations in an attempt to ensure
+                    # that no two threads/processes send this message
+                    # simultaneously as well as attempting to not generate
+                    # spurious failure messages in the log; a diagram that
+                    # represents these operations is included in a
+                    # comment above this class
+                    try:
+                        # find the age of the tmp file (if it exists)
+                        age = None
+                        mtime = os.stat(tmp_filename)[stat.ST_MTIME]
+                        age = time.time() - mtime
+                    except OSError, e:
+                        if e.errno == 2: # file does not exist
+                            # the tmp file could not be stated because it
+                            # doesn't exist, that's fine, keep going
+                            pass
+                        else:
+                            # the tmp file could not be stated for some reason
+                            # other than not existing; we'll report the error
+                            raise
+
+                    # if the tmp file exists, check it's age
+                    if age is not None:
+                        try:
+                            if age > MAX_SEND_TIME:
+                                # the tmp file is "too old"; this suggests
+                                # that during an attemt to send it, the
+                                # process died; remove the tmp file so we
+                                # can try again
+                                os.unlink(tmp_filename)
+                            else:
+                                # the tmp file is "new", so someone else may
+                                # be sending this message, try again later
+                                continue
+                            # if we get here, the file existed, but was too
+                            # old, so it was unlinked
+                        except OSError, e:
+                            if e.errno == 2: # file does not exist
+                                # it looks like someone else removed the tmp
+                                # file, that's fine, we'll try to deliver the
+                                # message again later
+                                continue
+
+                    # now we know that the tmp file doesn't exist, we need to
+                    # "touch" the message before we create the tmp file so the
+                    # mtime will reflect the fact that the file is being
+                    # processed (there is a race here, but it's OK for two or
+                    # more processes to touch the file "simultaneously")
+                    try:
+                        os.utime(filename, None)
+                    except OSError, e:
+                        if e.errno == 2: # file does not exist
+                            # someone removed the message before we could
+                            # touch it, no need to complain, we'll just keep
+                            # going
+                            continue
+
+                    # creating this hard link will fail if another process is
+                    # also sending this message
+                    try:
+                        #os.link(filename, tmp_filename)
+                        _os_link(filename, tmp_filename)
+                    except OSError, e:
+                        if e.errno == 17: # file exists, *nix
+                            # it looks like someone else is sending this
+                            # message too; we'll try again later
+                            continue
+                    except Exception, e:
+                        if e[0] == 183 and e[1] == 'CreateHardLink':
+                            # file exists, win32
+                            continue
+
+                    # read message file and send contents
+                    file = open(filename)
+                    message = file.read()
+                    file.close()
+                    fromaddr, toaddrs, message = self._parseMessage(message)
+                    # The next block is the only one that is sensitive to
+                    # interruptions.  Everywhere else, if this daemon thread
+                    # stops, we should be able to correctly handle a restart.
+                    # In this block, if we send the message, but we are
+                    # stopped before we unlink the file, we will resend the
+                    # message when we are restarted.  We limit the likelihood
+                    # of this somewhat by using a lock to link the two
+                    # operations.  When the process gets an interrupt, it
+                    # will call the atexit that we registered (``stop``
+                    # below).  This will try to get the same lock before it
+                    # lets go.  Because this can cause the daemon thread to
+                    # continue (that is, to not act like a daemon thread), we
+                    # still use the _stopped flag to communicate.
+                    self._lock.acquire()
+                    try:
+                        try:
+                            self.mailer.send(fromaddr, toaddrs, message)
+                        except smtplib.SMTPResponseException, e:
+                            if 500 <= e.smtp_code <= 599:
+                                # permanent error, ditch the message
+                                self.log.error(
+                                    "Discarding email from %s to %s due to"
+                                    " a permanent error: %s",
+                                    fromaddr, ", ".join(toaddrs), str(e))
+                                #os.link(filename, rejected_filename)
+                                _os_link(filename, rejected_filename)
+                            else:
+                                # Log an error and retry later
+                                raise
+
+                        try:
+                            os.unlink(filename)
+                        except OSError, e:
+                            if e.errno == 2: # file does not exist
+                                # someone else unlinked the file; oh well
+                                pass
+                            else:
+                                # something bad happend, log it
+                                raise
+                    finally:
+                        self._lock.release()
+                    try:
+                        os.unlink(tmp_filename)
+                    except OSError, e:
+                        if e.errno == 2: # file does not exist
+                            # someone else unlinked the file; oh well
+                            pass
+                        else:
+                            # something bad happend, log it
+                            raise
+
+                    # TODO: maybe log the Message-Id of the message sent
+                    self.log.info("Mail from %s to %s sent.",
+                                  fromaddr, ", ".join(toaddrs))
+                    # Blanket except because we don't want
+                    # this thread to ever die
+                except:
+                    if fromaddr != '' or toaddrs != ():
+                        self.log.error(
+                            "Error while sending mail from %s to %s.",
+                            fromaddr, ", ".join(toaddrs), exc_info=True)
+                    else:
+                        self.log.error(
+                            "Error while sending mail : %s ",
+                            filename, exc_info=True)
+            else:
+                if forever:
+                    time.sleep(self.interval)
+
+            # A testing plug
+            if not forever:
+                break
+
+    def stop(self):
+        self._stopped = True
+        self._lock.acquire()
+        self._lock.release()


Property changes on: zope.sendmail/trunk/src/zope/sendmail/queue.py
___________________________________________________________________
Added: cvs2svn:cvs-rev
   + 1.1
Added: svn:keywords
   + Id
Added: svn:mergeinfo
   + 
Added: svn:eol-style
   + native

Modified: zope.sendmail/trunk/src/zope/sendmail/tests/test_delivery.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/tests/test_delivery.py	2010-01-07 06:31:54 UTC (rev 107769)
+++ zope.sendmail/trunk/src/zope/sendmail/tests/test_delivery.py	2010-01-07 07:11:43 UTC (rev 107770)
@@ -18,11 +18,8 @@
 $Id$
 """
 
-import os.path
-import shutil
 import smtplib
-from tempfile import mkdtemp
-from unittest import TestCase, TestSuite, makeSuite
+from unittest import TestCase, TestSuite, makeSuite, main
 
 import transaction
 from zope.testing import doctest
@@ -202,7 +199,7 @@
         self.infos = []
         self.errors = []
 
-    def getLogger(name):
+    def getLogger(self, name):
         return self
 
     def error(self, msg, *args, **kwargs):
@@ -304,120 +301,13 @@
         self.assertEquals(len(MaildirWriterStub.aborted_messages), 1)
 
 
-class TestQueueProcessorThread(TestCase):
-
-    def setUp(self):
-        from zope.sendmail.delivery import QueueProcessorThread
-        self.md = MaildirStub('/foo/bar/baz')
-        self.thread = QueueProcessorThread()
-        self.thread.setMaildir(self.md)
-        self.mailer = MailerStub()
-        self.thread.setMailer(self.mailer)
-        self.thread.log = LoggerStub()
-        self.dir = mkdtemp()
-
-    def tearDown(self):
-        shutil.rmtree(self.dir)
-
-    def test_parseMessage(self):
-        hdr = ('X-Zope-From: foo at example.com\n'
-               'X-Zope-To: bar at example.com, baz at example.com\n')
-        msg = ('Header: value\n'
-               '\n'
-               'Body\n')
-        f, t, m = self.thread._parseMessage(hdr + msg)
-        self.assertEquals(f, 'foo at example.com')
-        self.assertEquals(t, ('bar at example.com', 'baz at example.com'))
-        self.assertEquals(m, msg)
-
-    def test_deliveration(self):
-        self.filename = os.path.join(self.dir, 'message')
-        temp = open(self.filename, "w+b")
-        temp.write('X-Zope-From: foo at example.com\n'
-                   'X-Zope-To: bar at example.com, baz at example.com\n'
-                   'Header: value\n\nBody\n')
-        temp.close()
-        self.md.files.append(self.filename)
-        self.thread.run(forever=False)
-        self.assertEquals(self.mailer.sent_messages,
-                          [('foo at example.com',
-                            ('bar at example.com', 'baz at example.com'),
-                            'Header: value\n\nBody\n')])
-        self.failIf(os.path.exists(self.filename), 'File exists')
-        self.assertEquals(self.thread.log.infos,
-                          [('Mail from %s to %s sent.',
-                            ('foo at example.com',
-                             'bar at example.com, baz at example.com'),
-                            {})])
-
-    def test_error_logging(self):
-        self.thread.setMailer(BrokenMailerStub())
-        self.filename = os.path.join(self.dir, 'message')
-        temp = open(self.filename, "w+b")
-        temp.write('X-Zope-From: foo at example.com\n'
-                   'X-Zope-To: bar at example.com, baz at example.com\n'
-                   'Header: value\n\nBody\n')
-        temp.close()
-        self.md.files.append(self.filename)
-        self.thread.run(forever=False)
-        self.assertEquals(self.thread.log.errors,
-                          [('Error while sending mail from %s to %s.',
-                            ('foo at example.com',
-                             'bar at example.com, baz at example.com'),
-                            {'exc_info': 1})])
-
-    def test_smtp_response_error_transient(self):
-        # Test a transient error
-        self.thread.setMailer(SMTPResponseExceptionMailerStub(451))
-        self.filename = os.path.join(self.dir, 'message')
-        temp = open(self.filename, "w+b")
-        temp.write('X-Zope-From: foo at example.com\n'
-                   'X-Zope-To: bar at example.com, baz at example.com\n'
-                   'Header: value\n\nBody\n')
-        temp.close()
-        self.md.files.append(self.filename)
-        self.thread.run(forever=False)
-
-        # File must remail were it was, so it will be retried
-        self.failUnless(os.path.exists(self.filename))
-        self.assertEquals(self.thread.log.errors,
-                          [('Error while sending mail from %s to %s.',
-                            ('foo at example.com',
-                             'bar at example.com, baz at example.com'),
-                            {'exc_info': 1})])
-
-    def test_smtp_response_error_permanent(self):
-        # Test a permanent error
-        self.thread.setMailer(SMTPResponseExceptionMailerStub(550))
-        self.filename = os.path.join(self.dir, 'message')
-        temp = open(self.filename, "w+b")
-        temp.write('X-Zope-From: foo at example.com\n'
-                   'X-Zope-To: bar at example.com, baz at example.com\n'
-                   'Header: value\n\nBody\n')
-        temp.close()
-        self.md.files.append(self.filename)
-        self.thread.run(forever=False)
-
-        # File must be moved aside
-        self.failIf(os.path.exists(self.filename))
-        self.failUnless(os.path.exists(os.path.join(self.dir,
-                                                    '.rejected-message')))
-        self.assertEquals(self.thread.log.errors,
-                          [('Discarding email from %s to %s due to a '
-                            'permanent error: %s',
-                            ('foo at example.com',
-                             'bar at example.com, baz at example.com',
-                             "(550, 'Serious Error')"), {})])
-
-
 def test_suite():
     return TestSuite((
         makeSuite(TestMailDataManager),
         makeSuite(TestDirectMailDelivery),
         makeSuite(TestQueuedMailDelivery),
-        makeSuite(TestQueueProcessorThread),
         doctest.DocTestSuite(),
         ))
 
 if __name__ == '__main__':
-    unittest.main()
+    main()

Modified: zope.sendmail/trunk/src/zope/sendmail/tests/test_directives.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/tests/test_directives.py	2010-01-07 06:31:54 UTC (rev 107769)
+++ zope.sendmail/trunk/src/zope/sendmail/tests/test_directives.py	2010-01-07 07:11:43 UTC (rev 107770)
@@ -29,8 +29,8 @@
 
 from zope.sendmail.interfaces import \
      IMailDelivery, IMailer, ISMTPMailer
-from zope.sendmail.delivery import QueueProcessorThread
 from zope.sendmail import delivery
+from zope.sendmail.queue import QueueProcessorThread
 import zope.sendmail.tests
 
 

Copied: zope.sendmail/trunk/src/zope/sendmail/tests/test_queue.py (from rev 106770, zope.sendmail/trunk/src/zope/sendmail/tests/test_delivery.py)
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/tests/test_queue.py	                        (rev 0)
+++ zope.sendmail/trunk/src/zope/sendmail/tests/test_queue.py	2010-01-07 07:11:43 UTC (rev 107770)
@@ -0,0 +1,143 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Mail Delivery Tests
+
+Simple implementation of the MailDelivery, Mailers and MailEvents.
+
+$Id$
+"""
+
+import os.path
+import shutil
+
+from tempfile import mkdtemp
+from unittest import TestCase, TestSuite, makeSuite, main
+
+from zope.sendmail.tests.test_delivery import MaildirStub, LoggerStub, \
+    BrokenMailerStub, SMTPResponseExceptionMailerStub, MailerStub
+
+
+class TestQueueProcessorThread(TestCase):
+
+    def setUp(self):
+        from zope.sendmail.queue import QueueProcessorThread
+        self.md = MaildirStub('/foo/bar/baz')
+        self.thread = QueueProcessorThread()
+        self.thread.setMaildir(self.md)
+        self.mailer = MailerStub()
+        self.thread.setMailer(self.mailer)
+        self.thread.log = LoggerStub()
+        self.dir = mkdtemp()
+
+    def tearDown(self):
+        shutil.rmtree(self.dir)
+
+    def test_parseMessage(self):
+        hdr = ('X-Zope-From: foo at example.com\n'
+               'X-Zope-To: bar at example.com, baz at example.com\n')
+        msg = ('Header: value\n'
+               '\n'
+               'Body\n')
+        f, t, m = self.thread._parseMessage(hdr + msg)
+        self.assertEquals(f, 'foo at example.com')
+        self.assertEquals(t, ('bar at example.com', 'baz at example.com'))
+        self.assertEquals(m, msg)
+
+    def test_deliveration(self):
+        self.filename = os.path.join(self.dir, 'message')
+        temp = open(self.filename, "w+b")
+        temp.write('X-Zope-From: foo at example.com\n'
+                   'X-Zope-To: bar at example.com, baz at example.com\n'
+                   'Header: value\n\nBody\n')
+        temp.close()
+        self.md.files.append(self.filename)
+        self.thread.run(forever=False)
+        self.assertEquals(self.mailer.sent_messages,
+                          [('foo at example.com',
+                            ('bar at example.com', 'baz at example.com'),
+                            'Header: value\n\nBody\n')])
+        self.failIf(os.path.exists(self.filename), 'File exists')
+        self.assertEquals(self.thread.log.infos,
+                          [('Mail from %s to %s sent.',
+                            ('foo at example.com',
+                             'bar at example.com, baz at example.com'),
+                            {})])
+
+    def test_error_logging(self):
+        self.thread.setMailer(BrokenMailerStub())
+        self.filename = os.path.join(self.dir, 'message')
+        temp = open(self.filename, "w+b")
+        temp.write('X-Zope-From: foo at example.com\n'
+                   'X-Zope-To: bar at example.com, baz at example.com\n'
+                   'Header: value\n\nBody\n')
+        temp.close()
+        self.md.files.append(self.filename)
+        self.thread.run(forever=False)
+        self.assertEquals(self.thread.log.errors,
+                          [('Error while sending mail from %s to %s.',
+                            ('foo at example.com',
+                             'bar at example.com, baz at example.com'),
+                            {'exc_info': 1})])
+
+    def test_smtp_response_error_transient(self):
+        # Test a transient error
+        self.thread.setMailer(SMTPResponseExceptionMailerStub(451))
+        self.filename = os.path.join(self.dir, 'message')
+        temp = open(self.filename, "w+b")
+        temp.write('X-Zope-From: foo at example.com\n'
+                   'X-Zope-To: bar at example.com, baz at example.com\n'
+                   'Header: value\n\nBody\n')
+        temp.close()
+        self.md.files.append(self.filename)
+        self.thread.run(forever=False)
+
+        # File must remail were it was, so it will be retried
+        self.failUnless(os.path.exists(self.filename))
+        self.assertEquals(self.thread.log.errors,
+                          [('Error while sending mail from %s to %s.',
+                            ('foo at example.com',
+                             'bar at example.com, baz at example.com'),
+                            {'exc_info': 1})])
+
+    def test_smtp_response_error_permanent(self):
+        # Test a permanent error
+        self.thread.setMailer(SMTPResponseExceptionMailerStub(550))
+        self.filename = os.path.join(self.dir, 'message')
+        temp = open(self.filename, "w+b")
+        temp.write('X-Zope-From: foo at example.com\n'
+                   'X-Zope-To: bar at example.com, baz at example.com\n'
+                   'Header: value\n\nBody\n')
+        temp.close()
+        self.md.files.append(self.filename)
+        self.thread.run(forever=False)
+
+        # File must be moved aside
+        self.failIf(os.path.exists(self.filename))
+        self.failUnless(os.path.exists(os.path.join(self.dir,
+                                                    '.rejected-message')))
+        self.assertEquals(self.thread.log.errors,
+                          [('Discarding email from %s to %s due to a '
+                            'permanent error: %s',
+                            ('foo at example.com',
+                             'bar at example.com, baz at example.com',
+                             "(550, 'Serious Error')"), {})])
+
+
+def test_suite():
+    return TestSuite((
+        makeSuite(TestQueueProcessorThread),
+        ))
+
+if __name__ == '__main__':
+    main()


Property changes on: zope.sendmail/trunk/src/zope/sendmail/tests/test_queue.py
___________________________________________________________________
Added: cvs2svn:cvs-rev
   + 1.1
Added: svn:keywords
   + Id
Added: svn:mergeinfo
   + 
Added: svn:eol-style
   + native

Modified: zope.sendmail/trunk/src/zope/sendmail/zcml.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/zcml.py	2010-01-07 06:31:54 UTC (rev 107769)
+++ zope.sendmail/trunk/src/zope/sendmail/zcml.py	2010-01-07 07:11:43 UTC (rev 107770)
@@ -25,9 +25,9 @@
 from zope.schema import TextLine, BytesLine, Int, Bool
 
 from zope.sendmail.delivery import QueuedMailDelivery, DirectMailDelivery
-from zope.sendmail.delivery import QueueProcessorThread
 from zope.sendmail.interfaces import IMailer, IMailDelivery
 from zope.sendmail.mailer import SMTPMailer
+from zope.sendmail.queue import QueueProcessorThread
 
 try:
     from zope.component.security import proxify



More information about the checkins mailing list