From b94675985731bdd206ff92902c231c404c6e0590 Mon Sep 17 00:00:00 2001 From: Stuart Gathman <stuart@gathman.org> Date: Fri, 10 Jun 2011 01:39:59 +0000 Subject: [PATCH] Document threading limitations and show multiprocessing example. --- Milter/__init__.py | 15 +++++++++------ README | 3 +-- doc/mainpage.py | 19 ++++++++++++++++++- milter-template.py | 24 ++++++++++++++++++++---- pymilter.spec | 6 ++++-- 5 files changed, 52 insertions(+), 15 deletions(-) diff --git a/Milter/__init__.py b/Milter/__init__.py index 26eecf5..0e80797 100755 --- a/Milter/__init__.py +++ b/Milter/__init__.py @@ -28,6 +28,7 @@ def uniqueID(): _seq_lock.release() return seqno +## @private OPTIONAL_CALLBACKS = { 'connect':(P_NR_CONN,P_NOCONNECT), 'hello':(P_NR_HELO,P_NOHELO), @@ -40,6 +41,7 @@ OPTIONAL_CALLBACKS = { 'header':(P_NR_HDR,P_NOHDRS) } +## @private def decode_mask(bits,names): t = [ (s,getattr(milter,s)) for s in names] nms = [s for s,m in t if bits & m] @@ -47,6 +49,13 @@ def decode_mask(bits,names): if bits: nms += hex(bits) return nms +## @fn set_flags(flags) +# @brief Enable optional %milter actions. +# Certain %milter actions need to be enabled before calling milter.runmilter() +# or they throw an exception. +# @param flags Bit ored mask of optional actions to enable + + ## Class decorator to enable optional protocol steps. # P_SKIP is enabled by default when supported, but # applications may wish to enable P_HDR_LEADSPC @@ -538,12 +547,6 @@ class Milter(Base): # change in configuration. factory = Milter -## @fn set_flags(flags) -# @brief Enable optional %milter actions. -# Certain %milter actions need to be enabled before calling milter.runmilter() -# or they throw an exception. -# @param flags Bit ored mask of optional actions to enable - ## @private # @brief Connect context to connection instance and return enabled callbacks. def negotiate_callback(ctx,opts): diff --git a/README b/README index 45dd4ec..c4fc663 100644 --- a/README +++ b/README @@ -69,8 +69,7 @@ Not-so-quick Installation First install Sendmail. Make sure you read libmilter/README in the Sendmail source directory, and make sure you enable libmilter before you build. The 8.11 series had libmilter marked as FFR (For Future Release); 8.12 -officially -supports libmilter, but it's still not built by default. +officially supports libmilter, but it's still not built by default. Install Python, and enable threading in Modules/Setup. diff --git a/doc/mainpage.py b/doc/mainpage.py index e63f2b2..f8d31b8 100644 --- a/doc/mainpage.py +++ b/doc/mainpage.py @@ -1,6 +1,5 @@ ## @mainpage Writing Milters in Python # -# # At the lowest level, the <code>milter</code> module provides a thin wrapper # around the <a href="https://www.milter.org/developers/api/index"> sendmail # libmilter API</a>. This API lets you register callbacks for a number of @@ -34,3 +33,21 @@ # The <code>mime</code> module provides a wrapper for the Python email package # that fixes some bugs, and simplifies modifying selected parts of a MIME # message. +# +# @section threading +# +# The libmilter library which pymilter wraps +# <a href="https://www.milter.org/developers/overview#SignalHandling">handles +# all signals</a> itself, and expects to be called from a single main thread. +# It handles SIGTERM, SIGHUP, and SIGINT, mapping the first two to +# <a href="https://www.milter.org/developers/api/smfi_stop">smfi_stop</a> +# and the last to an internal ABORT. +# +# If you use python threads or threading modules, then signal handling gets +# confused. Threads may still be useful, but you may need to provide an +# alternate means of causing graceful shutdown. +# +# You may find the +# <a href="http://docs.python.org/release/2.6.6/library/multiprocessing.html"> +# multiprocessing</a> module useful. It can be a drop-in +# replacement for threading as illustrated in @ref milter-template.py. diff --git a/milter-template.py b/milter-template.py index d995c17..ee72380 100644 --- a/milter-template.py +++ b/milter-template.py @@ -14,7 +14,13 @@ import email import sys from socket import AF_INET, AF_INET6 from Milter.utils import parse_addr +if True: + from multiprocessing import Process as Thread, Queue +else: + from threading import Thread + from Queue import Queue +logq = Queue(maxsize=4) class myMilter(Milter.Base): @@ -117,16 +123,24 @@ class myMilter(Milter.Base): ## === Support Functions === def log(self,*msg): - print "%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S'),self.id), + logq.put((msg,self.id,time.time())) + +def background(): + while True: + t = logq.get() + if not t: break + msg,id,ts = t + print "%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S',time.localtime(ts)),id), # 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ... for i in msg: print i, print - ## === def main(): - socketname = "/tmp/pythonsock" + bt = Thread(target=background) + bt.start() + socketname = "/home/stuart/pythonsock" timeout = 600 # Register to have the Milter factory create instances of your class: Milter.factory = myMilter @@ -136,7 +150,9 @@ def main(): Milter.set_flags(flags) # tell Sendmail which features we use print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S') sys.stdout.flush() - Milter.runmilter("test",socketname,timeout) + Milter.runmilter("pythonfilter",socketname,timeout) + logq.put(None) + bt.join() print "%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S') if __name__ == "__main__": diff --git a/pymilter.spec b/pymilter.spec index 09f3a85..8a7b70b 100644 --- a/pymilter.spec +++ b/pymilter.spec @@ -13,8 +13,9 @@ License: GPLv2+ Group: Development/Libraries BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root Url: http://www.bmsi.com/python/milter.html -Requires: %{pythonbase}, sendmail >= 8.13 -# Need python2.4 specific pydns, not the version for system python +# python-2.6.4 gets RuntimeError: not holding the import lock +Requires: %{pythonbase} >= 2.6.5, sendmail >= 8.13 +# Need python2.6 specific pydns, not the version for system python Requires: %{pythonbase}-pydns # Needed for callbacks, not a core function but highly useful for milters BuildRequires: ed, %{pythonbase}-devel, sendmail-devel >= 8.13 @@ -77,6 +78,7 @@ rm -rf $RPM_BUILD_ROOT * Wed Mar 02 2010 Stuart Gathman <stuart@bmsi.com> 0.9.5-1 - Print milter.error for invalid callback return type. (Since stacktrace is empty, the TypeError exception is confusing.) +- Fix milter-template.py * Wed Mar 02 2010 Stuart Gathman <stuart@bmsi.com> 0.9.4-1 - Handle IP6 in Milter.utils.iniplist() -- GitLab