0

I have put together a function send_email [1] to send emails with support for plain-text and html messages. It works well, but I have an issue I don't quite know how to debug. My system has sendmail as its MTA.

The function is called in a for loop, as follows:

for data in data_set:
    subject, message = process(data)  # Irrelevant stuff

    send_email(subject, message, "fixed@email.address")

In the debug output of smtplib [1] I see that all calls to send_email completed successfully. The weird behavior is:

  • If the message is "short" (I tested with a single line), all sent messages actually arrive to fixed@email.address
  • If the message is "not short" (that is, the multiple lines I generate with the real process_data function), only the first email does arrive, while the others don't, even though the debug output of smtplib in [1] reports success for each and all of the emails.
  • If the message is equally "not short" but the destination address is different for each message, then all messages arrive to their intended destinations.

For the latter case, the for loop would look like:

addresses = ["fixed@email.address", "fixed.2@email.address", ...]
for data, addr in zip(data_set, addresses):
    subject, message = process(data)  # Irrelevant stuff

    send_email(subject, message, addr)

The intended behavior is of course different addresses for different data, but I'm concerned that not understanding why this happens might bite me in an unexpected way later on.

[1] My send mail function:

import smtplib
import socket
import getpass
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def send_email (subject, message, to, reply_to='', cc='', html_message=''):

    COMMASPACE = ", "
    user, host = get_user_and_host_names()
    sender = '%s@%s' % (user, host)

    receivers = make_address_list(to)
    copies = make_address_list(cc)

    msg = MIMEMultipart('alternative')

    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = COMMASPACE.join(receivers)
    if reply_to:
        msg.add_header('Reply-to', reply_to)
    if len(copies):
        msg.add_header('CC', COMMASPACE.join(copies))

    # According to RFC 2046, the last part of a multipart message, in this case
    # the HTML message, is best and preferred.
    if message:
        msg.attach( MIMEText(message, 'plain'))
    if html_message:
        msg.attach( MIMEText(html_message, 'html'))

    smtpObj = smtplib.SMTP('localhost')
    smtpObj.set_debuglevel(1)
    smtpObj.sendmail(sender, receivers, msg.as_string())
    smtpObj.quit()
    print "\nSuccessfully sent email to:", COMMASPACE.join(receivers)

def get_user_and_host_names():
    user = getpass.getuser()
    host = socket.gethostname()
    return user, host

def make_address_list (addresses):
    if isinstance(addresses, str):
        receivers = addresses.replace(' ','').split(',')
    elif isinstance(addresses, list):
        receivers = addresses
    return receivers
Luis E.
  • 841
  • 11
  • 15

2 Answers2

0

A working solution I had for sending email:

from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders
import smtplib

class EMail(object):
        """ Class defines method to send email
        """
        def __init__(self, mailFrom, server, usrname, password, files, debug=False):
                self.debug = debug
                self.mailFrom = mailFrom
                self.smtpserver = server
                self.EMAIL_PORT = 587
                self.usrname = usrname
                self.password = password


        def sendMessage(self, subject, msgContent, files, mailto):
            """ Send the email message

                Args:
                    subject(string): subject for the email
                    msgContent(string): email message Content
                    files(List): list of files to be attached
                    mailto(string): email address to be sent to
            """

            msg = self.prepareMail(subject, msgContent, files, mailto)

            # connect to server and send email
            server=smtplib.SMTP(self.smtpserver, port=self.EMAIL_PORT)
            server.ehlo()
            # use encrypted SSL mode
            server.starttls()
            # to make starttls work
            server.ehlo()
            server.login(self.usrname, self.password)
            server.set_debuglevel(self.debug)
            try:
                failed = server.sendmail(self.mailFrom, mailto, msg.as_string())
            except Exception as er:
                print er
            finally:
                server.quit()

        def prepareMail(self, subject, msgHTML, attachments, mailto):
            """ Prepare the email to send
                Args:
                    subject(string): subject of the email.
                    msgHTML(string): HTML formatted email message Content.
                    attachments(List): list of file paths to be attached with email. 
            """
            msg = MIMEMultipart()
            msg['From'] = self.mailFrom
            msg['To'] = mailto
            msg['Date'] = formatdate(localtime=True)
            msg['Subject'] = subject

            #the Body message
            msg.attach(MIMEText(msgHTML, 'html'))
            msg.attach(MIMEText("Add signature here"))
            if attachments:
                for phile in attachments:
                        # we could check for MIMETypes here
                        part = MIMEBase('application',"octet-stream")
                        part.set_payload(open(phile, "rb").read())
                        Encoders.encode_base64(part)
                        part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(phile))
                        msg.attach(part)
            return msg

I hope this helps.

Ciasto piekarz
  • 7,853
  • 18
  • 101
  • 197
0

My Working solution to format your list in this manner (after many hours of experiment):

["firstemail@mail.com", "secondmail@mail.com", "thirdmail@email.com"]

I used:

    to_address = list(str(self.to_address_list).split(","))

to convert my QString into string then into list with splitting with a ","

In my case COMMASPACE was not working, because on splitting a space was already added by default.

Sjon
  • 4,989
  • 6
  • 28
  • 46