Reputation: 716
I'm trying to send emails with different messages to different receivers via libsmtp and gmail in a loop.
I tried to send mail without closing smtp connection. It just send all message to same receiver but I would like to send messages with receivers. I tried to open and quit connection for each loop but after first iteration executes and quits the connection, second iteration gives error message "smtplib.SMTPServerDisconnected: please run connect() first" even though it should have connect again.
class Mail:
msg = MIMEMultipart()
def __init__(self, addr_from, addr_to):
self.msg['From'] = addr_from
self.msg['To'] = addr_to
def set_subject(self, subject):
self.msg['Subject'] = subject
def set_message(self, message):
self.msg.attach(MIMEText(message, 'plain'))
def attach_image(self, image_location, image_name):
fp = open(image_location, 'rb')
img = MIMEImage(fp.read())
img.add_header('Content-Disposition', "attachment; filename= %s" % image_name)
fp.close()
self.msg.attach(img)
def get_mail(self):
return self.msg
class EmailServer:
server = smtplib.SMTP('smtp.gmail.com', 587)
def __init__(self, sender_mail, password):
self.sender_mail = sender_mail
self.password = password
self.server.ehlo()
self.server.starttls()
self.server.login(sender_mail, password)
def send_mail(self, mail):
self.server.sendmail(mail['From'], mail['To'], mail.as_string())
def quit_server(self):
self.server.quit()
for row in range(0,x):
name = sheet.cell_value(row, 0)
mail_adress = sheet.cell_value(row, 1)
print(name, mail_adress)
mail = Mail('[email protected]', mail_adress)
mail.set_subject("subject")
mail.set_message('message')
server = EmailServer('[email protected]', 'xxxxxxxxx')
mail.attach_image('/Users/xxxx/xxxx/xxxx.jpg', 'title')
server.send_mail(mail.get_mail())
server.quit_server()
Upvotes: 1
Views: 219
Reputation: 23743
The way you defined Mail
, msg
is a class attribute so each instance of Mail
contains the same msg
.
import email
from email.mime.multipart import MIMEMultipart
class Mail:
msg = MIMEMultipart()
def __init__(self, addr_from, addr_to):
self.msg['From'] = addr_from
self.msg['To'] = addr_to
for afrom, ato in zip(list('abcd'),list('wert')):
mail = Mail(afrom, ato)
print(id(mail.msg))
In [60]:
160675264
160675264
160675264
160675264
MIMEMultipart is an email.message
object; its __setitem__
docstring states: ...does not overwrite an existing header with the same field name. Use __delitem__() first to delete any existing headers.
...
In [61]: help(mail.msg.__setitem__)
Help on method __setitem__ in module email.message:
__setitem__(name, val) method of email.mime.multipart.MIMEMultipart instance
Set the value of a header.
Note: this does not overwrite an existing header with the same field
name. Use __delitem__() first to delete any existing headers.
After the for loop, the From
and To
headers are lists with multiple values:
mail.msg.get_all('From'), mail.msg.get_all('To')
Out[62]: (['a', 'b', 'c', 'd'], ['w', 'e', 'r', 't'])
One solution to your problem would be to delete the From
and To
headers before assigning a new value in the loop.
In [63]: del mail.msg['From']
In [64]: del mail.msg['To']
In [65]: mail.msg.get_all('From'), mail.msg.get_all('To')
Out[65]: (None, None)
In [66]: mail.msg['From'] = 'z'
In [67]: mail.msg['To'] = 'y'
In [68]: mail.msg.get_all('From'), mail.msg.get_all('To')
Out[68]: (['z'], ['y'])
Another option might be to create a new msg
for each Mail
instance.
class Mail:
def __init__(self, addr_from, addr_to):
self.msg = MIMEMultipart()
self.msg['From'] = addr_from
self.msg['To'] = addr_to
Another option would be to send the mail AFTER the loop. SMTP.sendmail
's to_addrs
parameter accepts a list so EmailServer.sendmail
needs to be modified
class EmailServer:
...
...
def send_mail(self, mail):
self.server.sendmail(mail['From'], mail.get_all('To'), mail.as_string())
Then After a loop that adds multiple 'To'
addresses
server.send_mail(mail.get_mail())
server.quit_server()
I'm not able to test this one but it should work. You should probably create just one Mail
instance with all the stuff that doesn't change; and one EmailServer
instance then use the loop to add multiple address to the Mail
instance. Then send the mail.
Upvotes: 1