[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