[Checkins] SVN: zope.sendmail/trunk/ Provide a console script ``zope-sendmail`` which can be used to process the

Fabio Tranchitella kobold at kobold.it
Thu Jan 7 05:37:47 EST 2010


Log message for revision 107776:
  Provide a console script ``zope-sendmail`` which can be used to process the
  delivery queue in case processorThread is False. The console script can either
  process the messages in the queue once, or run in "daemon" mode.
  
  
  

Changed:
  U   zope.sendmail/trunk/CHANGES.txt
  U   zope.sendmail/trunk/setup.py
  U   zope.sendmail/trunk/src/zope/sendmail/queue.py
  U   zope.sendmail/trunk/src/zope/sendmail/tests/test_queue.py

-=-
Modified: zope.sendmail/trunk/CHANGES.txt
===================================================================
--- zope.sendmail/trunk/CHANGES.txt	2010-01-07 10:27:24 UTC (rev 107775)
+++ zope.sendmail/trunk/CHANGES.txt	2010-01-07 10:37:46 UTC (rev 107776)
@@ -2,7 +2,7 @@
 CHANGES
 =======
 
-3.6.2 (unreleased)
+3.7.0 (unreleased)
 ------------------
 
 - Removed dependency on ``zope.security``: the security support is optional,
@@ -17,6 +17,10 @@
   directive: if False, the QueueProcessorThread is not started and thus an
   independent process must process the queue; it defaults to True for b/c.
 
+- Provide a console script ``zope-sendmail`` which can be used to process the
+  delivery queue in case processorThread is False. The console script can
+  either process the messages in the queue once, or run in "daemon" mode.
+
 3.6.1 (2009-11-16)
 ------------------
 

Modified: zope.sendmail/trunk/setup.py
===================================================================
--- zope.sendmail/trunk/setup.py	2010-01-07 10:27:24 UTC (rev 107775)
+++ zope.sendmail/trunk/setup.py	2010-01-07 10:37:46 UTC (rev 107776)
@@ -54,4 +54,8 @@
                        ],
       include_package_data = True,
       zip_safe = False,
+      entry_points="""
+      [console_scripts]
+      zope-sendmail = zope.sendmail.queue:run
+      """
       )

Modified: zope.sendmail/trunk/src/zope/sendmail/queue.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/queue.py	2010-01-07 10:27:24 UTC (rev 107775)
+++ zope.sendmail/trunk/src/zope/sendmail/queue.py	2010-01-07 10:37:46 UTC (rev 107776)
@@ -11,15 +11,16 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Mail Delivery utility implementation
+"""Queue processor thread
 
-This module contains various implementations of `MailDeliverys`.
+This module contains the queue processor thread.
 
 $Id$
 """
 __docformat__ = 'restructuredtext'
 
 import atexit
+import ConfigParser
 import logging
 import os
 import smtplib
@@ -28,6 +29,7 @@
 import time
 
 from zope.sendmail.maildir import Maildir
+from zope.sendmail.mailer import SMTPMailer
 
 import sys
 if sys.platform == 'win32':
@@ -97,7 +99,7 @@
 
 class QueueProcessorThread(threading.Thread):
     """This thread is started at configuration time from the
-    `mail:queuedDelivery` directive handler.
+    `mail:queuedDelivery` directive handler if processorThread is True.
     """
 
     log = logging.getLogger("QueueProcessorThread")
@@ -317,3 +319,171 @@
         self._stopped = True
         self._lock.acquire()
         self._lock.release()
+
+
+def boolean(s):
+    s = str(s).lower()
+    return s.startswith("t") or s.startswith("y") or s.startswith("1")
+
+
+def string_or_none(s):
+    if s == 'None':
+        return None
+    return s
+
+
+class ConsoleApp(object):
+    """Allows running of Queue Processor from the console."""
+
+    _usage = """%(script_name)s [OPTIONS] path/to/maildir
+
+    OPTIONS:
+
+        --daemon            Run in daemon mode, periodically checking queue
+                            and sending messages.  Default is to send all
+                            messages in queue once and exit.
+
+        --interval <#secs>  How often to check queue when in daemon mode.
+                            Default is 3 seconds.
+
+        --hostname          Name of smtp host to use for delivery.  Default is
+                            localhost.
+
+        --port              Which port on smtp server to deliver mail to.
+                            Default is 25.
+
+        --username          Username to use to log in to smtp server.  Default
+                            is none.
+
+        --password          Password to use to log in to smtp server.  Must be
+                            specified if username is specified.
+
+        --force-tls         Do not connect if TLS is not available.  Not
+                            enabled by default.
+
+        --no-tls            Do not use TLS even if is available.  Not enabled
+                            by default.
+
+        --config <inifile>  Get configuration from specificed ini file; it must
+                            contain a section [app:zope-sendmail].
+
+    """
+
+    _error = False
+    daemon = False
+    interval = 3
+    hostname = 'localhost'
+    port = 25
+    username = None
+    password = None
+    force_tls = False
+    no_tls = False
+    queue_path = None
+
+    def __init__(self, argv=sys.argv, verbose=True):
+        self.script_name = argv[0]
+        self.verbose = verbose
+        self._process_args(argv[1:])
+        self.mailer = SMTPMailer(self.hostname, self.port, self.username,
+            self.password, self.no_tls, self.force_tls)
+
+    def main(self):
+        if self._error:
+            return
+        queue = QueueProcessorThread(self.interval)
+        queue.setMailer(self.mailer)
+        queue.setQueuePath(self.queue_path)
+        queue.run(forever=self.daemon)
+
+    def _process_args(self, args):
+        got_queue_path = False
+        while args:
+            arg = args.pop(0)
+            if arg == "--daemon":
+                self.daemon = True
+            elif arg == "--interval":
+                try:
+                    self.interval = float(args.pop(0))
+                except:
+                    self._error_usage()
+            elif arg == "--hostname":
+                if not args:
+                    self._error_usage()
+                self.hostname = args.pop(0)
+            elif arg == "--port":
+                try:
+                    self.port = int(args.pop(0))
+                except:
+                    self._error_usage()
+            elif arg == "--username":
+                if not args:
+                    self._error_usage()
+                self.username = args.pop(0)
+            elif arg == "--password":
+                if not args:
+                    self._error_usage()
+                self.password = args.pop(0)
+            elif arg == "--force-tls":
+                self.force_tls = True
+            elif arg == "--no-tls":
+                self.no_tls = True
+            elif arg == "--config":
+                if not args:
+                    self._error_usage()
+                self._load_config(args.pop(0))
+            elif arg.startswith("-") or got_queue_path:
+                self._error_usage()
+            else:
+                self.queue_path = arg
+                got_queue_path = True
+        if not self.queue_path:
+            self._error_usage()
+        if (self.username or self.password) and \
+           not (self.username and self.password):
+            if self.verbose:
+                print >>sys.stderr, "Must use username and password together."
+            self._error = True
+        if self.force_tls and self.no_tls:
+            if self.verbose:
+                print >>sys.stderr, \
+                    "--force-tls and --no-tls are mutually exclusive."
+            self._error = True
+
+    def _load_config(self, path):
+        section = "app:zope-sendmail"
+        names = [
+            "interval",
+            "hostname",
+            "port",
+            "username",
+            "password",
+            "force_tls",
+            "no_tls",
+            "queue_path",
+        ]
+        defaults = dict([(name, str(getattr(self, name))) for name in names])
+        config = ConfigParser.ConfigParser(defaults)
+        config.read(path)
+        self.interval = float(config.get(section, "interval"))
+        self.hostname = config.get(section, "hostname")
+        self.port = int(config.get(section, "port"))
+        self.username = string_or_none(config.get(section, "username"))
+        self.password = string_or_none(config.get(section, "password"))
+        self.force_tls = boolean(config.get(section, "force_tls"))
+        self.no_tls = boolean(config.get(section, "no_tls"))
+        self.queue_path = string_or_none(config.get(section, "queue_path"))
+
+    def _error_usage(self):
+        if self.verbose:
+            print >>sys.stderr, self._usage % {"script_name": self.script_name,}
+        self._error = True
+
+
+def run():
+    logging.basicConfig()
+    app = ConsoleApp()
+    app.main()
+
+
+if __name__ == "__main__":
+    run_console()

Modified: zope.sendmail/trunk/src/zope/sendmail/tests/test_queue.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/tests/test_queue.py	2010-01-07 10:27:24 UTC (rev 107775)
+++ zope.sendmail/trunk/src/zope/sendmail/tests/test_queue.py	2010-01-07 10:37:46 UTC (rev 107776)
@@ -24,6 +24,7 @@
 from tempfile import mkdtemp
 from unittest import TestCase, TestSuite, makeSuite, main
 
+from zope.sendmail.queue import ConsoleApp
 from zope.sendmail.tests.test_delivery import MaildirStub, LoggerStub, \
     BrokenMailerStub, SMTPResponseExceptionMailerStub, MailerStub
 
@@ -133,10 +134,129 @@
                              'bar at example.com, baz at example.com',
                              "(550, 'Serious Error')"), {})])
 
+test_ini = """[app:zope-sendmail]
+interval = 33
+hostname = testhost
+port = 2525
+username = Chris
+password = Rossi
+force_tls = False
+no_tls = True
+queue_path = hammer/dont/hurt/em
+"""
 
+class TestConsoleApp(TestCase):
+    def setUp(self):
+        from zope.sendmail.delivery import QueuedMailDelivery
+        from zope.sendmail.maildir import Maildir
+        self.dir = mkdtemp()
+        self.queue_dir = os.path.join(self.dir, "queue")
+        self.delivery = QueuedMailDelivery(self.queue_dir)
+        self.maildir = Maildir(self.queue_dir, True)
+        self.mailer = MailerStub()
+        
+    def tearDown(self):
+        shutil.rmtree(self.dir)
+
+    def test_args_processing(self):
+        # simplest case that works
+        cmdline = "zope-sendmail %s" % self.dir
+        app = ConsoleApp(cmdline.split(), verbose=False)
+        self.assertEquals("zope-sendmail", app.script_name)
+        self.assertFalse(app._error)
+        self.assertEquals(self.dir, app.queue_path)
+        self.assertFalse(app.daemon)
+        self.assertEquals(3, app.interval)
+        self.assertEquals("localhost", app.hostname)
+        self.assertEquals(25, app.port)
+        self.assertEquals(None, app.username)
+        self.assertEquals(None, app.password)
+        self.assertFalse(app.force_tls)
+        self.assertFalse(app.no_tls)
+        # simplest case that doesn't work
+        cmdline = "zope-sendmail"
+        app = ConsoleApp(cmdline.split(), verbose=False)
+        self.assertEquals("zope-sendmail", app.script_name)
+        self.assertTrue(app._error)
+        self.assertEquals(None, app.queue_path)
+        self.assertFalse(app.daemon)
+        self.assertEquals(3, app.interval)
+        self.assertEquals("localhost", app.hostname)
+        self.assertEquals(25, app.port)
+        self.assertEquals(None, app.username)
+        self.assertEquals(None, app.password)
+        self.assertFalse(app.force_tls)
+        self.assertFalse(app.no_tls)
+        # use (almost) all of the options
+        cmdline = "zope-sendmail --daemon --interval 7 --hostname foo --port 75 " \
+            "--username chris --password rossi --force-tls " \
+            "%s" % self.dir
+        app = ConsoleApp(cmdline.split(), verbose=False)
+        self.assertEquals("zope-sendmail", app.script_name)
+        self.assertFalse(app._error)
+        self.assertEquals(self.dir, app.queue_path)
+        self.assertTrue(app.daemon)
+        self.assertEquals(7, app.interval)
+        self.assertEquals("foo", app.hostname)
+        self.assertEquals(75, app.port)
+        self.assertEquals("chris", app.username)
+        self.assertEquals("rossi", app.password)
+        self.assertTrue(app.force_tls)
+        self.assertFalse(app.no_tls)
+        # test username without password
+        cmdline = "zope-sendmail --username chris %s" % self.dir
+        app = ConsoleApp(cmdline.split(), verbose=False)
+        self.assertTrue(app._error)
+        # test --tls and --no-tls together
+        cmdline = "zope-sendmail --tls --no-tls %s" % self.dir
+        app = ConsoleApp(cmdline.split(), verbose=False)
+        self.assertTrue(app._error)
+        # test force_tls and no_tls
+        comdline = "zope-sendmail --force-tls --no-tls %s" % self.dir
+        self.assertTrue(app._error)
+        
+    def test_ini_parse(self):
+        ini_path = os.path.join(self.dir, "zope-sendmail.ini")
+        f = open(ini_path, "w")
+        f.write(test_ini)
+        f.close()
+        # override most everything
+        cmdline = """zope-sendmail --config %s""" % ini_path
+        app = ConsoleApp(cmdline.split(), verbose=False)
+        self.assertEquals("zope-sendmail", app.script_name)
+        self.assertFalse(app._error)
+        self.assertEquals("hammer/dont/hurt/em", app.queue_path)
+        self.assertFalse(app.daemon)
+        self.assertEquals(33, app.interval)
+        self.assertEquals("testhost", app.hostname)
+        self.assertEquals(2525, app.port)
+        self.assertEquals("Chris", app.username)
+        self.assertEquals("Rossi", app.password)
+        self.assertFalse(app.force_tls)
+        self.assertTrue(app.no_tls)
+        # override nothing, make sure defaults come through
+        f = open(ini_path, "w")
+        f.write("[app:zope-sendmail]\n\nqueue_path=foo\n")
+        f.close()
+        cmdline = """zope-sendmail --config %s %s""" % (ini_path, self.dir)
+        app = ConsoleApp(cmdline.split(), verbose=False)
+        self.assertEquals("zope-sendmail", app.script_name)
+        self.assertFalse(app._error)
+        self.assertEquals(self.dir, app.queue_path)
+        self.assertFalse(app.daemon)
+        self.assertEquals(3, app.interval)
+        self.assertEquals("localhost", app.hostname)
+        self.assertEquals(25, app.port)
+        self.assertEquals(None, app.username)
+        self.assertEquals(None, app.password)
+        self.assertFalse(app.force_tls)
+        self.assertFalse(app.no_tls)
+
+
 def test_suite():
     return TestSuite((
         makeSuite(TestQueueProcessorThread),
+        makeSuite(TestConsoleApp),
         ))
 
 if __name__ == '__main__':



More information about the checkins mailing list