Emailing Python Script Errors

After my workshop, one of the attendees asked me how he would go about mailing the results or errors from a geoprocessing script. I did some hand waving about smtplib and traceback, two of Python's standard libraries, and will follow up now with some concrete, tested code.

In the example below we import three modules that will be used to access Python exceptions and stack tracebacks, format them as strings, and interface with an email server:

import smtplib
import traceback
import StringIO

def process():
    """This function performs an invalid list access, raising an error"""
    return [1, 2][3]

def mail_error(msg):
    """Mail an error message"""
    server = smtplib.SMTP('mail.sgillies.net')
    server.sendmail('overnight@sgillies.net', ['overnight-admin@sgillies.net'],
                    msg)

The process() function is a stand-in for whatever function is doing your geoprocessing. In this example, all it does is make an invalid list access, and thereby cause an error (an IndexError) to be raised. The mail_error() function makes a connection to a local mail server and sends the specified message from overnight to overnight-admin. The from address does not necessarily have to be a valid address unless your system has extremely tight security. Make sure to use the proper mail host for your own system. See the smtplib docs for more details.

Next, define a mail message template, and the main script body:

ERROR_TEMPLATE = """
Subject: Processing Error

Processing has failed. Traceback and error are shown below:

%s
"""

try:
    result = process()
except:
    exc_string = StringIO.StringIO()
    traceback.print_exc(file=exc_string)
    message = ERROR_TEMPLATE % (exc_string.getvalue())
    mail_error(message)

The error template has one string "slot", into which will be interpolated a system traceback. In the body of the script, we call the process function. All exceptions are caught, we use the print_exc() function of the traceback module to get the current stack traceback, and then pass this traceback as a string to the mail_error() function.

print_exc() requires a Python file-like object as an argument. This example uses Python's handy StringIO class to make an in-memory file that is named exc_string. The traceback is written to this file, we interpolate the contents of the file (getvalue) into the template, and then send the interpolated template via email.

Email is only one option, of course. Doing geoprocessing with Python rather than VB means that you can mix in all kinds of great standard and non-standard functionality. You might syndicate processing errors using RSS, generate stats and plots using matplotlib, or ping your enterprise's paging system.

Update: Howard reminds to make a stronger point about the error template. The templates drives the email, and Reply-To:, CC: and such can be added along with the subject. He also points out that many SMTP servers use pop-before-smtp to authenticate email senders. In this case, you'd want to look into poplib.