I'm making a function in Python that sends email with the attachment being optional. However, when I try to send a file, it gives the error "AttributeError: 'str' object has no attribute 'get_all'".
I looked at other Stack Overflow posts and other sites for help. I took all their advice and yet, I'm still getting the same error. What can I try next?
Full error:
Traceback (most recent call last):
File "/Users/XXX/Desktop/device_control_virus/emailing.py", line 65, in <module>
email("testing", "testing testing", "[email protected]", "logs.txt")
File "/Users/XXX/Desktop/device_control_virus/emailing.py", line 60, in email
s.send_message(text)
File "/Users/XXX/opt/anaconda3/lib/python3.8/smtplib.py", line 926, in send_message
resent = msg.get_all('Resent-Date')
AttributeError: 'str' object has no attribute 'get_all'
Here's the code:
from email import message
import smtplib
from email.message import EmailMessage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from email.message import EmailMessage
def email(subject, body, to, attachment=None):
if attachment == None:
msg = EmailMessage()
msg.set_content(body)
msg['subject'] = subject
msg['to'] = to
user = "[email protected]"
msg['from'] = user
password = "XXXXXXXXX"
s = smtplib.SMTP("smtp.gmail.com", 587)
s.ehlo()
s.starttls()
s.login(user, password)
s.send_message(msg)
s.quit()
else:
msg = MIMEMultipart()
msg['subject'] = subject
msg['to'] = ', '.join(to)
user = "[email protected]"
msg['from'] = user
password = "XXXXXXXXX"
msg.attach(MIMEText(body, 'plain'))
filename = attachment
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= %s" % filename)
msg.attach(part)
s = smtplib.SMTP("smtp.gmail.com", 587)
s.ehlo()
s.starttls()
s.login(user, password)
text = msg.as_string()
s.send_message(text)
s.quit()
if __name__ == '__main__':
email("testing", "testing testing", "XXXXXXXXX", "logs.txt")
CodePudding user response:
The problem is that you are passing a string to send_message, but it expects a Message
object. You should be able to fix your problem by changing this:
text = msg.as_string()
s.send_message(text)
To this:
s.send_message(msg)
Edit
I think your "503 5.5.1 RCPT first" problem may be due to the to
field in your message. You are using the code msg['to'] = ', '.join(to)
, which expects a list of email addresses. However, when you call it, you are passing in the single string "XXXXXXXXX".
When you call join
with a string, it joins together all the characters, as in Python you can iterate through a string by character. This means ', '.join("XXXXXXXXX")
will give you the string X, X, X, X, X, X, X, X, X
, i.e. a list of characters in your email address, instead of the single email address you were probably expecting. Instead, try setting the to
field like msg['to'] = to
.
CodePudding user response:
Your code rather horribly mixes the modern EmailMessage
API with the legacy MIMEMultipart
etc email
classes from Python pre 3.6. The new API is much simpler and more versatile, and you should simply not use the old unless you specifically need to support Python 3.2 or older (the new API was introduced but not yet preferred in 3.3).
I haven't attempted to debug your code in detail; I would recommend that you simply ditch it and start over, then troubleshoot whatever new problems you may encounter.
from email.message import EmailMessage # just once should suffice
import smtplib
def email(subject, body, to, attachment=None):
msg = EmailMessage()
msg.set_content(body)
msg['subject'] = subject
msg['to'] = to
user = "[email protected]"
msg['from'] = user
password = "XXXXXXXXX"
if attachment is not None:
with open(attachment, 'rb') as fp:
msg.add_attachment(
fp.read(),
maintype='attachment',
subtype='octet-stream',
filename=attachment)
with smtplib.SMTP("smtp.gmail.com", 587) as s:
s.ehlo()
s.starttls()
s.login(user, password)
s.send_message(msg)
s.quit()
if __name__ == '__main__':
email("testing", "testing testing", "XXXXXXXXX", "logs.txt")
You would still need to replace the to
address with a valid email address, and perhaps verify that the steps and the parameters in the smtplib
dialog with Gmail are correct.
(Also, the correct MIME type for a text file would be text/plain
; but application/octet-stream
is of course always a viable content type, especially if your code needs to handle arbitrary file types.)