smtplib, capturing output from set_debuglevel ??

Josiah Carlson jcarlson at uci.edu
Fri Oct 8 13:35:16 EDT 2004


> > Steve, Andrew, Josiah,
> > 
> > Thanks for your answers.
> > 
> > The outgoing SMTP is multi-threaded,   so the threads disrupt each other's
> > output to the file-like object  if there is any concurrent outgoing mail
> > activity.
> > 
> > Is there anyway I can seperate the output from each thread and capture it ?
> > 
> > The outgoing threads are started  with
> > 
> > thread.start_new_thread(outThread,(args,None))
> > 
> > One thread per out-going message.  Each handles the outgoing message sending
> > and error-handling for the message.   I can't talk back to the calling
> > thread/class because it is the incoming SMTP part and it is destroyed when
> > the remote client connection is dropped.  (The SMTP server is a filtering
> > relay)
> > 
> AT this point I might be tempted to modify the smtplib code so it 
> doesn't use sys.stdout for its debug output, but some other object that 
> can be passed to SMTP.__init__().
> 
> Since sys.stdout is going to be common to all modules, I think you have 
> highlighted a weakness in the smtplib debugging paradigm. But if some 
> smart type can come up with a better solution I'd love to see it.


Instantiate a copy of multi_thread_output (defined below), and set it to
the sys.stdout.

Every once and a while, call its 'get_ready()' method to get the output
for a thread.  If there are none ready, it will return None.


 - Josiah


Example use:

>>> t = multi_thread_output()
>>> def st(i):
...     for j in xrange(100):
...             print >>t, "message %i from %i"%(j, i)
...             time.sleep(.01)
...
>>> import time
>>> for i in xrange(10):
...     threading.Thread(target=st, args=(i,)).start()
...
>>> t.get_ready().read()
'message 0 from 9\n message 1 from 9\n message 2 from 9\nmessage 3 from 9\nmessa
ge 4 from 9\nmessage 5 from 9\nmessage 6 from 9\nmessage 7 from 9\nmessage 8 fro
m 9\nmessage 9 from 9\nmessage 10 from 9\nmessage 11 from 9\nmessage 12 from 9\n
[lines cut]

#----------actual code below-----------

import threading
from StringIO import StringIO

class multi_thread_output:
    def __init__(self):
        self.lock = threading.Lock()
        self.ios = {}
    def write(self, data):
        thread = threading.currentThread().getName()
        try:
            self.lock.acquire()
            if thread in self.ios:
                self.ios[thread].write(data)
            else:
                t = self.ios[thread] = StringIO()
                t.write(data)
        finally:
            self.lock.release()
    def get_ready(self):
        names = {}
        for th in threading.enumerate():
            names[th.getName()] = None
        try:
            self.lock.acquire()
            for name in self.ios:
                if name not in names:
                    r = self.ios.pop(name)
                    r.seek(0)
                    return r
        finally:
            self.lock.release()

Blah blah, this work placed in the public domain, blah blah.  If you use
it heavily, at least pretend to reference me.




More information about the Python-list mailing list