Making imaplib simple

23 Jun 2009

Ataraxia Consulting


Not long ago I wrote an imap polling script for work that used Python’s imaplib. Shortly after that HoopyCat wrote an excellent imap backup script that also used imaplib. We exchanged some stories and opined on wanting a simpler mechanism to get imaplib to return an email.Message object (a feature that should be available from the core).

I happened to have to look at my code again this week and decided to work up a quick example of the interface I expected to actually find in the core imaplib

import imaplib
import email
 
class __simplebase:
  def get_messages_by_folder(self, folder, charset=None):
    ids = self.get_ids_by_folder(folder, charset=charset)
 
    for m in self.get_messages_by_ids(ids):
      yield m
 
  def get_ids_by_folder(self, folder, charset=None):
    self.select(folder)
    status, data = self.search(charset, 'ALL')
    if status != 'OK':
      raise Exception(data)
 
    return data[0].split()
 
  def get_messages_by_ids(self, ids):
    for i in ids:
      yield self.get_message_by_id(i)
 
  def get_message_by_id(self, id):
    status, data = self.fetch(id, '(RFC822)')
 
    if status != 'OK':
      raise Exception(data)
 
    return email.message_from_string(data[0][1])
 
class SimpleImap(imaplib.IMAP4, __simplebase):
  pass
 
class SimpleImapSSL(imaplib.IMAP4_SSL, __simplebase):
  pass

The SimpleImap and SimpleImapSSL classes should be drop in replacements for your existing usage of IMAP4 and IMAP4_SSL. You’ll notice the use of generator objects (the yield keyword), this means that each iteration the message you’re working on is pulled from the server right then. That’s useful when you have a lot of messages that you don’t necessarily want to cache into memory or when they’ll potentially have large attachments. On the other hand, it will result in considerably more imap commands and traffic than if you were to just pull all the messages at once.

Here’s a sample usage that prints out the subject of every message in your inbox:

c = SimpleImap('mail.example.com', 143)
c.login('someluser', 'stupersekrit')
for m in c.get_messages_by_folder('INBOX'):
  print m['subject']
c.logout()