Credit: Brent Burley
Suppose you need to poll an IMAP inbox for unread messages and
display the sender and subject in a scrollable window using Tkinter.
The key functionality you need is in the standard Python module
imaplib
, with some help from the
rfc822
module. Example 10-4
reads the server name, user, and password from the
~/.imap
file. They must be all on one line,
separated by spaces.
The hard (and interesting) part of developing this program was figuring out how to get the IMAP part working, which took a fair bit of investigating. The most productive approach to understanding the IMAP protocol proved to be talking to the IMAP server directly from a Python interactive session to see what it returned:
>>> import imaplib >>> M = imaplib.IMAP4(imap_server) >>> M.login(imap_user, imap_password) ('OK', ['LOGIN complete']) >>> M.select(readonly=1) ('OK', ['8']) >>> M.search(None, '(UNSEEN UNDELETED)') ('OK', ['8']) >>> M.fetch(8, '(BODY[HEADER.FIELDS (SUBJECT FROM)])') ('OK', [('8 (BODY[HEADER.FIELDS (SUBJECT FROM)] {71}', 'From: John Doe <[email protected]> Subject: test message '), ')'])
Interactive exploration is so simple with Python because of excellent interactive environments such as the standard interactive session (with readline and completion) or IDEs such as IDLE. As such, it is often the best way to clarify one’s doubts or any ambiguities one may find in the documentation.
Example 10-4. Watching for new IMAP mail with a GUI
import imaplib, string, sys, os, re, rfc822 from Tkinter import * PollInterval = 60 # seconds def getimapaccount( ): try: f = open(os.path.expanduser('~/.imap')) except IOError, e: print 'Unable to open ~/.imap: ', e sys.exit(1) global imap_server, imap_user, imap_password try: imap_server, imap_user, imap_password = string.split(f.readline( )) except ValueError: print 'Invalid data in ~/.imap' sys.exit(1) f.close( ) class msg: # a file-like object for passing a string to rfc822.Message def _ _init_ _(self, text): self.lines = string.split(text, ' 15 12') self.lines.reverse( ) def readline(self): try: return self.lines.pop( ) + ' ' except: return '' class Mailwatcher(Frame): def _ _init_ _(self, master=None): Frame._ _init_ _(self, master) self.pack(side=TOP, expand=YES, fill=BOTH) self.scroll = Scrollbar(self) self.list = Listbox(self, font='7x13', yscrollcommand=self.scroll.set, setgrid=1, height=6, width=80) self.scroll.configure(command=self.list.yview) self.scroll.pack(side=LEFT, fill=BOTH) self.list.pack(side=LEFT, expand=YES, fill=BOTH) def getmail(self): self.after(1000*PollInterval, self.getmail) self.list.delete(0,END) try: M = imaplib.IMAP4(imap_server) M.login(imap_user, imap_password) except Exception, e: self.list.insert(END, 'IMAP login error: ', e) return try: result, message = M.select(readonly=1) if result != 'OK': raise Exception, message typ, data = M.search(None, '(UNSEEN UNDELETED)') for num in string.split(data[0]): try: f = M.fetch(num, '(BODY[HEADER.FIELDS (SUBJECT FROM)])') m = rfc822.Message(msg(f[1][0][1]), 0) subject = m['subject'] except KeyError: f = M.fetch(num, '(BODY[HEADER.FIELDS (FROM)])') m = rfc822.Message(msg(f[1][0][1]), 0) subject = '(no subject)' fromaddr = m.getaddr('from') if fromaddr[0] == "": n = fromaddr[1] else: n = fromaddr[0] text = '%-20.20s %s' % (n, subject) self.list.insert(END, text) len = self.list.size( ) if len > 0: self.list.see(len-1) except Exception, e: self.list.delete(0,END) print sys.exc_info( ) self.list.insert(END, 'IMAP read error: ', e) M.logout( ) if _ _name_ _=='_ _main_ _': getimapaccount( ) root = Tk(className='mailwatcher') root.title('mailwatcher') mw = Mailwatcher(root) mw.getmail( ) mw.mainloop( )
Documentation for the standard library modules
imaplib
and rfc822
in the
Library Reference; information about Tkinter can
be obtained from a variety of sources, such as
Pythonware’s An Introduction to Tkinter, by Fredrik Lundh (http://www.pythonware.com/library), New
Mexico Tech’s Tkinter reference
(http://www.nmt.edu/tcc/help/lang/python/docs.html),
and various books; the IMAP protocol is described in RFC 2060
(http://www.ietf.org/rfc/rfc1939.txt).