I want to send an email with an attachment using the following code (Python 3.1)
(greatly simplified to show the example)
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
msg = MIMEMultipart()
msg['From'] = from_addr
msg['To'] = to_addr
msg['Subject'] = subject
msg.attach(MIMEText(body))
fp = open(att_file)
msg1 = MIMEText(fp.read())
attachment = msg1.add_header('Content-Disposition', 'attachment', filename=att_file)
msg.attach(attachment)
# set string to be sent as 3rd parameter to smptlib.SMTP.sendmail()
send_string = msg.as_string()
The attachment object msg1 returns 'email.mime.text.MIMEText' object at ', but when the msg1.add_header(...) line runs the result is None, hence the program falls-over in msg.as_string() because no part of the attachment can have a None value. (Traceback shows "'NoneType' object has no attribute 'get_content_maintype'" in line 118 of _dispatch in generator.py, many levels down from msg.as_string())
Has anyone any idea what the cause of the problem might be? Any help would be appreciated.
Alan Harris-Reid
Use:
msg.attach(msg1)
Related
The email sends and shows that it sends to both listed recipients, but only the first listed one actually receives the message. Strange since I don't notice anything particularly wrong with how I entered the addresses in (based on other examples I came across), so I'm looking for another perspective on the issue.
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
email_user = 'myemail#gmail.com'
email_send = 'otheremail1#gmail.com, otheremail2#gmail.com'
subject = 'Test'
msg = MIMEMultipart()
msg['From'] = email_user
msg['To'] = email_send
msg['Subject'] = subject
body = """Hello,
This is a test.
Thanks!"""
msg.attach(MIMEText(body,'plain'))
filename='dataset.csv'
attachment =open(filename,'rb')
part = MIMEBase('application','octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',"attachment; filename= "+filename)
msg.attach(part)
text = msg.as_string()
server = smtplib.SMTP('smtp.office365.com',587)
server.starttls()
server.login(email_user,'password')
server.sendmail(email_user,email_send,text)
server.quit()
server.sendmail(email_user,email_send.split(','),text) and remove the space
Basically you only sent the first one and need to pass the other one.
Tried with only multiple to and multiple cc individually, which works fine but when i try both i get an error:
File
"path\Continuum\anaconda2\envs\mypython\lib\smtplib.py",
line 870, in sendmail senderrs[each] = (code, resp) TypeError:
unhashable type: 'list'"
Code:
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.application import MIMEApplication
strFrom = 'fasdf#dfs.com'
cc='abc.xyz#dfa.com, sdf.xciv#lfk.com'
to='sadf#sdfa.com,123.lfadf#fa.com'
msg = MIMEMultipart('related')
msg['Subject'] = 'Subject'
msg['From'] = strFrom
msg['To'] =to
msg['Cc']=cc
#msg['Bcc']= strBcc
msg.preamble = 'This is a multi-part message in MIME format.'
msgAlternative = MIMEMultipart('alternative')
msg.attach(msgAlternative)
msgText = MIMEText('This is the alternative plain text message.')
msgAlternative.attach(msgText)
msgText = MIMEText('''<html>
<body><p>Hello<p>
</body>
</html> '''.format(**locals()), 'html')
msgAlternative.attach(msgText)
import smtplib
smtp = smtplib.SMTP()
smtp.connect('smtp address')
smtp.ehlo()
smtp.sendmail(strFrom, to, msg.as_string())
smtp.quit()
Attaching To or from should be a string and sendmail should always be in the form of the list.
cc=['abc.xyz#dfa.com', 'sdf.xciv#lfk.com']
to=['sadf#sdfa.com','123.lfadf#fa.com']
msg['To'] =','.join(to)
msg['Cc']=','.join(cc)
toAddress = to + cc
smtp.sendmail(strFrom, toAddress, msg.as_string())
The to parameter should be a list of all the addresses you wish to send the message to. The division in To: and Cc: is basically for display purposes only; SMTP simply has a single sequence of recipients which translate to one RCPT TO command for each address.
def addresses(addrstring):
"""Split in comma, strip surrounding whitespace."""
return [x.strip() for x in addrstring.split(',')]
smtp.sendmail(strFrom, addresses(to) + addresses(cc), msg.as_string())
So I was working on an mail script to send mail to a list of users from text file. It works fine, iterating through the list, each user receives an email - except one detail, the subject is dropped from all emails except the last one sent by the script. What's happening here?
#!/usr/bin/env python
import sys
from smtplib import SMTP
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
targets = open('targets.txt', "rb")
def mailme():
for line in targets:
msg = MIMEMultipart('alternative')
m_source = '<sender#address.com>'
m_target = '%s' % line
smtp_server = 'mail.server.com'
smtp_port = '25'
msg['From'] = m_source
msg['To'] = m_target
msg['Subject'] = "Subject"
smtp = SMTP()
smtp.set_debuglevel(0)
smtp.connect(smtp_server, smtp_port)
message_text = ('Some Text for %s' % (m_target)
)
message_html = ("""<body>
<h1>Some HTML for %s</h1>
</body>"""
) % (m_target)
txt = MIMEText(message_text, 'plain')
web = MIMEText(message_html, 'html')
msg.attach(txt)
msg.attach(web)
smtp.sendmail(m_source, m_target, msg.as_string())
smtp.quit()
if __name__ == "__main__":
mailme()
sys.exit(0)
I suggested OP to put msg['Subject'] before msg['From'] and it solves the problem. I suggested because the python MIME email examples/tutorials all have msg['Subject'] comes before msg['From'] and msg['To'].
I thought it is a template to follow. If anyone knows of why this approach solves the problem please shed some light on this.
I keep trying to run this code in order to send an excel sheet as an attachment on an email. I can send normal emails using smtplib but can't get the MIMEMultipart to work. I keep getting the [AttributeError: 'list' object has no attribute 'encode'] error
import smtplib, ssl
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders
fromaddr = ['Email']
sendto = ['Email']
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = sendto
msg['Subject'] = 'This is cool'
body = "this is the body of the text message"
msg.attach(MIMEText(body, 'plain'))
filename = 'Work.xlsx'
attachment = open('/home/mark/Work.xlsx', 'rb')
part = MIMEBase('application', "octet-stream")
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename= %s' % filename)
msg.attach(part)
smtpObj = smtplib.SMTP('smtp.gmail.com', 587)
smtpObj.ehlo()
smtpObj.starttls()
smtpObj.login('email', 'password')
text = msg.as_string()
smtpObj.sendmail(fromaddr, sendto , text)
smtpObj.quit()
fromaddr = ['Email']
sendto = ['Email']
This looks a little odd to me. Shouldn't they be strings, not lists?
fromaddr = 'Email'
sendto = 'Email'
Still I was getting an error, so I did below changes and it worked for me.
toaddr = ['mailid_1','mailid_2']
cc = ['mailid_3','mailid_4']
bcc = ['mailid_5','mailid_6']
subject = 'Email from Python Code'
fromaddr = 'sender_mailid'
message = "\n !! Hello... !!"
msg['From'] = fromaddr
msg['To'] = ', '.join(toaddr)
msg['Cc'] = ', '.join(cc)
msg['Bcc'] = ', '.join(bcc)
msg['Subject'] = subject
s.sendmail(fromaddr, (toaddr+cc+bcc) , message)
There seems bug. For to email list. You need to handle it little differently.
The To message attribute you need as string, whereas the function for sending needs a list.
I see it pass with To attribute being list as well but outlook only first recipient is getting mail. Others though shown in To list but they never get any mails.
to_mail_list = ", ".join(to_mail)
msg['To'] = to_mail_list
smtp_obj.sendmail(from_mail, to_mail, msg.as_string())
I wrote the below code for sending an email to multiple addresses.... But i am able to send the mail for only first address in the list...could give me exact reason and solution for it. Thanks in advance!!
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from smtplib import SMTP
COMMASPACE = ', '
msg = MIMEMultipart()
msg['Subject'] = 'Test attaching mail'
msg['From'] = 'x#x.com'
msg['Reply-to'] = ''
msg['To'] = COMMASPACE.join(['x1#x.com','x2#x.com','x3#x.com'])
# That is what u see if dont have an email reader:
msg.preamble = 'Multipart massage.\n'
# This is the textual part:
part = MIMEText("Hello im sending an email from a python program")
msg.attach(part)
# This is the binary part(The Attachment):
file="../logs_usecase/TestUsecase.log"
part = MIMEApplication(open(file,"rb").read())
part.add_header('Content-Disposition', 'attachment', filename=file)
msg.attach(part)
# Create an instance in SMTP server
smtp = SMTP("smtp.gmail.com:587")
# Start the server:
smtp.starttls()
smtp.ehlo()
smtp.login('x#x.com', "xxxxx")
# Send the email
smtp.sendmail(msg['From'], msg['To'], msg.as_string())
SMTP.sendmail expects a list of recipients, but you're passing a string with concatenated email addresses:
SMTP.sendmail(from_addr, to_addrs, msg[, mail_options, rcpt_options])
Send mail. The required arguments are an RFC 822 from-address string, a list of RFC 822 to-address strings (a bare string will be treated as a list with 1 address), and a message string.
(emphasis added)
You should pass the same list you join with COMMASPACE directly to SMTP.sendmail instead of passing the concatenated flat string.