Your Linux Data Center Experts

The other day I had two different clients asking about processing e-mail from a python program. In particular, each e-mail message that comes in gets handed off to be processed by this program. Setting up the mail server to call the program is fairly easy, by configuring local delivery and using the .forward file or similar. However, the program which processes the message needs to do a number of things to do it's job reliably.

I pulled together some pieces of existing code I had to handle these issues. Read on for an example of my code for processing e-mail.

The main responsibilities that need to be handled are:

  • Report if a Traceback happens.
  • Give the system administrator the opportunity to correct Tracebacks and re-process the message before bouncing it.
  • Log errors and tracebacks.
  • Parse the incoming message.
  • Report success when the message is completely processed.

This sample includes the ability to report tracebacks via syslog directly, by providing information back to the mail server (via stderr, which many mail servers honor), and by e-mailing a traceback report.

Below is the code I came up with, the highlighted portion is where the custom processing would be done.

This source can be downloaded from the ftp.tummy.com FTP site under /pub/tummy/pythonmailrcpt. This code is placed in the public domain, feel free to use it.

Mail processing example:

  1. !/usr/bin/env python

  2. &nbsp_place_holder;Python program which receives e-mail.

  3. &nbsp_place_holder;

  4. import rfc822, syslog, sys, random, os

  5. &nbsp_place_holder;

  6. &nbsp_place_holder;

  7. class ExceptHook:

  8. &nbsp_place_holder; &nbsp_place_holder;########################################################################

  9. &nbspplace_holder; &nbsp_place_holder;def __init_(self, useSyslog = 1, useStderr = 0, emailRecipient = None):

  10. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; self.useSyslog = useSyslog

  11. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; self.useStderr = useStderr

  12. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; self.useEmail = emailRecipient

  13. &nbsp_place_holder;

  14. &nbsp_place_holder;

  15. &nbsp_place_holder; &nbsp_place_holder;#######################################

  16. &nbspplace_holder; &nbsp_place_holder;def __call_(self, etype, evalue, etb):

  17. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; import traceback, string

  18. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; tb = traceback.format_exception(*(etype, evalue, etb))

  19. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; tb = map(string.rstrip, tb)

  20. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; tb = string.join(tb, '\n')

  21. &nbsp_place_holder;

  22. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; if self.useEmail:

  23. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder;emailFp = os.popen('/usr/sbin/sendmail -t -oi', 'w')

  24. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder;emailFp.write('To: %s\n' % self.useEmail)

  25. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder;emailFp.write('From: %s\n' % self.useEmail)

  26. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder;emailFp.write('Subject: Traceback notification from %s\n' %

  27. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder;os.path.basename(sys.argv[0]))

  28. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder;emailFp.write('\n')

  29. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; else: emailFp = None

  30. &nbsp_place_holder;

  31. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; for line in string.split(tb, '\n'):

  32. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder;if emailFp: emailFp.write(line + '\n')

  33. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder;if self.useSyslog: syslog.syslog(line)

  34. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder;if self.useStderr: sys.stderr.write(line + '\n')

  35. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; if emailFp: emailFp.close()

  36. &nbsp_place_holder;

  37. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; sys.stderr.write('Error in processing e-mail.\n')

  38. &nbsp_place_holder;

  39. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; # &nbsp_place_holder;TEMPFAIL causes the delivery to be retried

  40. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; sys.exit(os.EX_TEMPFAIL)

  41. &nbsp_place_holder;

  42. &nbsp_place_holder;logging and exception handling

  43. syslog.openlog(os.path.basename(sys.argv[0]), syslog.LOG_PID, syslog.LOG_MAIL)

  44. sys.excepthook = ExceptHook(useSyslog = 1, useStderr = 0,

  45. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; emailRecipient = 'user@example.com')

  46. &nbsp_place_holder;

  47. &nbsp_place_holder;parse the incoming message

  48. (For Python versions <2.4)

  49. import rfc822

  50. headers = rfc822.Message(sys.stdin)

  51. (For Python versions >= 2.4)

  52. import email.parser

  53. headers = email.parser.Parser().parse(sys.stdin)

  54. &nbsp_place_holder;

  55. &nbsp_place_holder;process the message here

  56. syslog.syslog('Received mail from “%s”, subject: “%s”' % (

  57. &nbsp_place_holder; &nbsp_place_holder; &nbsp_place_holder; headers.get('From'), headers.get('Subject') ))

  58. &nbsp_place_holder;

  59. &nbsp_place_holder;demonstration of what happens when there's an error

  60. &nbsp_place_holder;generate an error 50% of the time

  61. if random.choice([ True, False ]): raise NotImplementedError('Test exception')

  62. &nbsp_place_holder;

  63. &nbsp_place_holder;successful return code

  64. sys.exit(0)

comments powered by Disqus

Join our other satisfied clients. Contact us today.