ActiveState Code

Recipe 576807: Send Email


A command line interface (CLI) program to send email.

Python
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# vim: et sw=4 ts=4 tw=79:
'''
Description: Send an email.
Author: sfw geek
Last Udpated: 2009-06-07.
'''


# Hungarian Variable Notation.
# a*    - Array.
# c*    - Constants.
# d*    - Dictionary.
# fo*   - File Object.
# i*    - Integer.
# l*    - List.
# st*   - Set.
# s*    - String.
# t*    - Tuple.


# MODULES.
import os
import sys
import smtplib
from datetime import datetime
from email.iterators import *
from optparse import OptionParser
from email.mime.text import MIMEText
from email.generator import Generator
from email.mime.multipart import MIMEMultipart
from email.utils import COMMASPACE, CRLF, formatdate


# CONSTANTS.
cERR_MSG_PREFIX = '*ERROR*: '


# DEFINITIONS.
def usage():
    '''Return a string detailing how this program is used.'''

    sProgramName = sys.argv[0]
    sUsage = '''

NAME:
    %s
SYNOPSIS:
    %s [OPTIONS]
DESCRIPTION:
    Sends an email.
    If the value to an argument is a file path and the file exists, the file will be read line by line and the values in the file will be used.
    When supplying multiple email addresses as an argument, a comma should be used to separate them.
    Arguments with spaces should be encolsed in double quotes (").''' % (sProgramName, sProgramName)

    return sUsage

def getArgs():
    '''Get program arguments.
Returns a tuple of arguement details.
The 1st value is a dict of options.
The 2nd value is a list of positional arguments.'''

    getOptParser = OptionParser(usage())
    getOptParser.add_option('-b', '--body', action='store', type='string', dest='body',
        help='Body of email. All lines from file used if file path provided.')
    getOptParser.add_option('-c', '--cc', action='store', type='string', dest='cc',
        help='Who the email is also to be sent to (CC). One email address per line if file path provided.')
    getOptParser.add_option('-d', '--debug', action='store_true', dest='debug',
        help='Increase verbosity to help debugging.')
    getOptParser.add_option('-f', '--from', action='store', type='string', dest='frm',
        help='Who the email is from. Only first line of file used if file path provided.')
    getOptParser.add_option('-m', '--machine', action='store', type='string', dest='smtphost',
        help='The name of SMTP host used to send the email. Only first line of file used if file path provided.')
    getOptParser.add_option('-s', '--subject', action='store', type='string', dest='subject',
        help='The subject of the email. Only first line of file used if file path provided.')
    getOptParser.add_option('-t', '--to', action='store', type='string', dest='to',
        help='Who the email is to be sent to. One email address per line if file path provided.')

    (dOpts, lPosArgs) = getOptParser.parse_args()

    if not (dOpts.smtphost and dOpts.to and dOpts.frm and dOpts.subject and dOpts.body and len(lPosArgs) == 0):
        getOptParser.print_help()
        sys.exit(2)

    return dOpts, lPosArgs

def checkPythonVersion(tMinVersion):
    '''Throw an exception if python interperter is less than the supplied version tuple.
e.g. (2, 6, 0, 'final', 0)'''

    iMinVerLen = len(tMinVersion)
    tPyVersion = sys.version_info # e.g. (2, 6, 0, 'final', 0)

    # Python interperter is less than supplied version error message.
    sErrMsg = '%sPython Version %s ' % (cERR_MSG_PREFIX, '.'.join(map(str, tMinVersion)))
    sErrMsg +=  "Or Higher Is Required To Run '%s'!" % (sys.argv[0])

    if iMinVerLen > 0 and tPyVersion[0] < tMinVersion[0]:
        raise RuntimeError, sErrMsg
    if iMinVerLen > 1 and tPyVersion[1] < tMinVersion[1]:
        raise RuntimeError, sErrMsg
    if iMinVerLen > 2 and tPyVersion[2] < tMinVersion[2]:
        raise RuntimeError, sErrMsg
    if iMinVerLen > 3 and tPyVersion[3] != tMinVersion[3]:
        raise RuntimeError, sErrMsg
    if iMinVerLen > 4 and tPyVersion[3] < tMinVersion[4]:
        raise RuntimeError, sErrMsg

def checkInput(sInput):
    '''Check if input is a path to a file. If it is read the file and return the contents.
The input is returned if it is not a file path that exists.'''

    lData = []
    if os.path.exists(sInput):
        foIn = open(sInput, 'r')
        for sLine in foIn:
            lData.append(sLine.strip())
        foIn.close()
    else:
        lData.append(sInput)

    return lData

def main():
    '''Program entry point.'''

    # Store when program started.
    dtStart = datetime.now()

    tArgs = getArgs()

    # Arguments.
    dOpts = tArgs[0]
    lArgs = tArgs[1]

    msg = MIMEMultipart()

    lFrom = checkInput(dOpts.frm)
    msg['From'] = lFrom[0]

    lTo = checkInput(dOpts.to)
    msg['To'] = COMMASPACE.join(lTo)

    if dOpts.cc:
        # Optional.
        lCc = checkInput(dOpts.cc)
        msg['Cc'] = COMMASPACE.join(lCc)

    #msg['Bcc'] = COMMASPACE.join(lBcc)

    msg['Date'] = formatdate(localtime=True)

    lSubject = checkInput(dOpts.subject)
    msg['Subject'] = lSubject[0]

    lBody = checkInput(dOpts.body)
    msg.attach(MIMEText(CRLF.join(lBody)))

    lSmtpHost = checkInput(dOpts.smtphost)
    smtpSvr = smtplib.SMTP(lSmtpHost[0])
    if dOpts.debug:
        # Increase display verbosity.
        smtpSvr.set_debuglevel(1)
    smtpSvr.sendmail(lFrom[0], lTo, msg.as_string())
    smtpSvr.quit()


if __name__ == '__main__':
    checkPythonVersion((2, 5))
    main()

Discussion

Provides an example of how to send email using OptionParser and smtplib modules. Handy for sending an email from a batch/script file once a process has completed. The ability to use files as arguments to the program is useful to easily configure email message components (to, cc, body, subject).

Known Issues:

  • source code uses Hungarian variable notation.
  • no functionality for sending attachments.

Sign in to comment