Document threading limitations and show multiprocessing example.

This commit is contained in:
Stuart Gathman
2011-06-10 01:39:59 +00:00
parent f6702e39dd
commit b946759857
5 changed files with 52 additions and 15 deletions
+9 -6
View File
@@ -28,6 +28,7 @@ def uniqueID():
_seq_lock.release() _seq_lock.release()
return seqno return seqno
## @private
OPTIONAL_CALLBACKS = { OPTIONAL_CALLBACKS = {
'connect':(P_NR_CONN,P_NOCONNECT), 'connect':(P_NR_CONN,P_NOCONNECT),
'hello':(P_NR_HELO,P_NOHELO), 'hello':(P_NR_HELO,P_NOHELO),
@@ -40,6 +41,7 @@ OPTIONAL_CALLBACKS = {
'header':(P_NR_HDR,P_NOHDRS) 'header':(P_NR_HDR,P_NOHDRS)
} }
## @private
def decode_mask(bits,names): def decode_mask(bits,names):
t = [ (s,getattr(milter,s)) for s in names] t = [ (s,getattr(milter,s)) for s in names]
nms = [s for s,m in t if bits & m] nms = [s for s,m in t if bits & m]
@@ -47,6 +49,13 @@ def decode_mask(bits,names):
if bits: nms += hex(bits) if bits: nms += hex(bits)
return nms 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. ## Class decorator to enable optional protocol steps.
# P_SKIP is enabled by default when supported, but # P_SKIP is enabled by default when supported, but
# applications may wish to enable P_HDR_LEADSPC # applications may wish to enable P_HDR_LEADSPC
@@ -538,12 +547,6 @@ class Milter(Base):
# change in configuration. # change in configuration.
factory = Milter 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 ## @private
# @brief Connect context to connection instance and return enabled callbacks. # @brief Connect context to connection instance and return enabled callbacks.
def negotiate_callback(ctx,opts): def negotiate_callback(ctx,opts):
+1 -2
View File
@@ -69,8 +69,7 @@ Not-so-quick Installation
First install Sendmail. Make sure you read libmilter/README in the Sendmail First install Sendmail. Make sure you read libmilter/README in the Sendmail
source directory, and make sure you enable libmilter before you build. The 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 8.11 series had libmilter marked as FFR (For Future Release); 8.12
officially officially supports libmilter, but it's still not built by default.
supports libmilter, but it's still not built by default.
Install Python, and enable threading in Modules/Setup. Install Python, and enable threading in Modules/Setup.
+18 -1
View File
@@ -1,6 +1,5 @@
## @mainpage Writing Milters in Python ## @mainpage Writing Milters in Python
# #
#
# At the lowest level, the <code>milter</code> module provides a thin wrapper # 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 # 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 # 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 # 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 # that fixes some bugs, and simplifies modifying selected parts of a MIME
# message. # 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.
+20 -4
View File
@@ -14,7 +14,13 @@ import email
import sys import sys
from socket import AF_INET, AF_INET6 from socket import AF_INET, AF_INET6
from Milter.utils import parse_addr 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): class myMilter(Milter.Base):
@@ -117,16 +123,24 @@ class myMilter(Milter.Base):
## === Support Functions === ## === Support Functions ===
def log(self,*msg): 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 ... # 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ...
for i in msg: print i, for i in msg: print i,
print print
## === ## ===
def main(): def main():
socketname = "/tmp/pythonsock" bt = Thread(target=background)
bt.start()
socketname = "/home/stuart/pythonsock"
timeout = 600 timeout = 600
# Register to have the Milter factory create instances of your class: # Register to have the Milter factory create instances of your class:
Milter.factory = myMilter Milter.factory = myMilter
@@ -136,7 +150,9 @@ def main():
Milter.set_flags(flags) # tell Sendmail which features we use Milter.set_flags(flags) # tell Sendmail which features we use
print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S') print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S')
sys.stdout.flush() 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') print "%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S')
if __name__ == "__main__": if __name__ == "__main__":
+4 -2
View File
@@ -13,8 +13,9 @@ License: GPLv2+
Group: Development/Libraries Group: Development/Libraries
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
Url: http://www.bmsi.com/python/milter.html Url: http://www.bmsi.com/python/milter.html
Requires: %{pythonbase}, sendmail >= 8.13 # python-2.6.4 gets RuntimeError: not holding the import lock
# Need python2.4 specific pydns, not the version for system python Requires: %{pythonbase} >= 2.6.5, sendmail >= 8.13
# Need python2.6 specific pydns, not the version for system python
Requires: %{pythonbase}-pydns Requires: %{pythonbase}-pydns
# Needed for callbacks, not a core function but highly useful for milters # Needed for callbacks, not a core function but highly useful for milters
BuildRequires: ed, %{pythonbase}-devel, sendmail-devel >= 8.13 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 * Wed Mar 02 2010 Stuart Gathman <stuart@bmsi.com> 0.9.5-1
- Print milter.error for invalid callback return type. - Print milter.error for invalid callback return type.
(Since stacktrace is empty, the TypeError exception is confusing.) (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 * Wed Mar 02 2010 Stuart Gathman <stuart@bmsi.com> 0.9.4-1
- Handle IP6 in Milter.utils.iniplist() - Handle IP6 in Milter.utils.iniplist()