Python smtplib: только первое письмо из нескольких доходит до адресата

Я собрал функцию send_email [1] для отправки электронных писем с поддержкой текстовых и HTML-сообщений. Это работает хорошо, но у меня есть проблема, которую я не совсем знаю, как отлаживать. Моя система имеет sendmail в качестве MTA.

Функция вызывается в цикле for следующим образом:

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

    send_email(subject, message, "[email protected]")

В выводе отладки smtplib [1] я вижу, что все вызовы send_email выполнены успешно. Странное поведение:

  • Если message "короткий" (я тестировал с одной строкой), все отправленные сообщения фактически поступают на [email protected]
  • Если message "не короткое" (то есть несколько строк, которые я генерирую с помощью настоящей функции process_data), приходит только первое электронное письмо, а остальные нет, несмотря на то, что вывод отладки из smtplib в [1] сообщает об успешном завершении каждого письма.
  • Если message в равной степени "не короткий" но адрес назначения разный для каждого сообщения, тогда все сообщения доставляются по назначению.

В последнем случае цикл for будет выглядеть так:

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

    send_email(subject, message, addr)

Предполагаемое поведение, конечно, разные адреса для разных данных, но я обеспокоен тем, что непонимание того, почему это происходит, может позже неожиданно укусить меня.

[1] Моя функция отправки почты:

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

person Luis E.    schedule 03.07.2014    source источник


Ответы (2)


Рабочее решение, которое у меня было для отправки электронной почты:

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

Надеюсь, это поможет.

person Ciasto piekarz    schedule 03.07.2014

Мое рабочее решение для форматирования вашего списка таким образом (после многих часов эксперимента):

["[email protected]", "[email protected]", "[email protected]"]

Я использовал:

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

чтобы преобразовать мою QString в строку, а затем в список с разделением на ","

В моем случае COMMASPACE не работал, потому что при разделении пробел уже был добавлен по умолчанию.

person Deepak Kumar Mahto    schedule 29.10.2015