The smtplib.server.sendmail function in python raises UnicodeEncodeError: 'ascii' codec can't encode character

Question:

I am trying to edit a text file then send it as email body using a python script but im getting the unicode encoding error. After some research i found the solution as using the method .encode(‘utf-8’) but this doesn’t serve me as the sendmail() method only sends strings

Here is the python code snippet Im using:

irtem = open('irtemplate.txt')
data = irtem.read().replace('(name)', eng_name).replace('(customer)', 
cu_name).replace('(sr)', SR_num).replace('(problem)', 
prob_description).replace('(email)', eng_email).replace('(details)', 
details_req).replace('(tele)', eng_tele)


message_text = data
message = "From: %srn" % fromaddr + "To: %srn" % toaddr + "CC: 
%srn" % ",".join(cc) + "Subject: %srn" % message_subject + "rn" + 
message_text
toaddrs = [toaddr] + cc + bcc
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddrs, message)
server.quit()

Traceback:

Traceback (most recent call last):
File "autoIR.py", line 39, in <module>
server.sendmail(fromaddr, toaddrs, message)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/smtplib.py", line 855, in sendmail
msg = _fix_eols(msg).encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode character 'u2019' in 
position 168: ordinal not in range(128)
Asked By: akshayKhanapuri

||

Answers:

smtplib.server‘s sendmail method expects a bytes instance; if it gets a str it tries to encode it to ASCII, resulting in a UnicodeEncodeError if the str contains any non-ASCII characters.

You can workaround this by encoding the message yourself:

>>> msg = 'Hello Wørld'
>>> from_ = '[email protected]'
>>> to_ = '[email protected]'
>>> subject = 'Hello'

>>> fmt = 'From: {}rnTo: {}rnSubject: {}rn{}'

>>> server.sendmail(to_, from_, fmt.format(to_, from_, subject, msg).encode('utf-8'))
{}

This will send this message*:

b'From: [email protected]'
b'To: [email protected]'
b'Subject: Hello'
b'Hello Wxc3xb8rld'

However this workaround will not work if you want to send non-text binary data with your message.

A better solution is to use the EmailMessage class from the email package.

>>> from email.message import EmailMessage
>>> em = EmailMessage()
>>> em.set_content(msg)
>>> em['To'] = to_
>>> em['From'] = from_
>>> em['Subject'] = subject

>>> # NB call the server's *send_message* method
>>> server.send_message(em)
{}

This sends this message; note the extra headers telling the recipient the encoding used:

b'Content-Type: text/plain; charset="utf-8"'
b'Content-Transfer-Encoding: 8bit'
b'MIME-Version: 1.0'
b'To: [email protected]'
b'From: [email protected]'
b'Subject: Hello'
b'X-Peer: ::1'
b''
b'Hello Wxc3xb8rld'

* Run the command python -m aiosmtpd -n -l localhost:1025† (Pre-Python3.9: python -m smtpd -n -c DebuggingServer localhost:1025) in a separate terminal to capture the message data.

aiosmtpd must be installed using pip. Unlike smtpd it is not part of the standard library.

Answered By: snakecharmerb