1

I want a Windows program that should be able to automatically send emails. I have written the code in Python, tested it locally on Fedora, then built an .exe using PyInstaller in a virtual German windows 10 and tried it there and it worked like a charm. Then I deployed the .exe on the target German Windows 10 machine and it always throws a UnicodeEncodeError. Here is the relevant part of my code:

msg = EmailMessage()
msg['From'] = username
msg['To'] = to_addr
msg['Subject'] = subject

msg.set_content(body, charset='utf-8')

with smtplib.SMTP(smtp_server, smtp_port) as server:
    if use_tls:
        server.starttls()

    server.login(self.username, self.password)
    server.send_message(msg)

And here is my error trace:

Traceback (most recent call last):
  File "main.py", line 68, in place_order
  File "order_mailer.py", line 133, in place_order
  File "smtplib.py", line 769, in starttls
  File "smtplib.py", line 611, in ehlo_or_helo_if_needed
  File "smtplib.py", line 451, in ehlo
  File "smtplib.py", line 378, in putcmd
  File "smtplib.py", line 357, in send
UnicodeEncodeError: 'ascii' codec can't encode character '\xfc' in position 10: ordinal not in range(128)

The Unicode character \xfc should be the German ü if I am right. I would need this character in the final deployment, but I did not use it in the subject neither the body or elsewhere in my test environment.

I tried encoding the headers:

msg['From'] = Header(username, 'utf-8').encode()
msg['To'] = Header(to_addr, 'utf-8').encode()
msg['Subject'] = Header(subject, 'utf-8').encode()

This still works in my virtual test system, but does not change anything on the real system.

EDIT: This question seems to be similar, but I am using the send_message function, not sendmail. I also tried switching to sendmail and handing it bytes, but this did not work. As the trace suggests, the error already occurs in starttls, before sending the message.

6
  • It would also be helpful if at least someone finds a way to reproduce the error, since I can't test and tweak the script directly on the target machine. Commented Sep 30 at 8:29
  • 1
    Are there any non-ascii characters in your machine's hostname(s)? Commented Sep 30 at 9:16
  • I thought the hostname would be localhost anyway, but maybe you are right. How do I find out the hostname of the target machine? Commented Sep 30 at 12:06
  • This question is similar to: Python3 'ascii' codec can't encode characters in position 135-136: ordinal not in range(128). If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Sep 30 at 13:00
  • Maybe first use print() (and print(type(...)), print(len(...)), etc.) to see which part of code is executed and what you really have in variables. It is called "print debugging" and it helps to see what code is really doing. Commented Sep 30 at 14:03

2 Answers 2

1

With the hint from snakecharmerb I found the solution myself. There was actually a German ü in the machine's hostname. The EHLO command tried to encode this in ASCII and failed. Since the hostname in the EHLO command is not verified in any way, I just removed any non-ASCII characters like this:

        try:
            local_hostname = socket.getfqdn().encode('ascii', 'ignore').decode('ascii')
            if not local_hostname:
                local_hostname = 'localhost'
        except:
            local_hostname = 'localhost'

        with smtplib.SMTP(smtp_server, smtp_port, local_hostname=local_hostname) as server:
            if use_tls:
                server.starttls()

            server.login(username, password)
            server.send_message(msg)
Sign up to request clarification or add additional context in comments.

Comments

0

I am working on the assumption that the username and to_addr values might be passed in the form of "First-Name Last-Name <[email protected]>" and may contain non-ASCII characters.

To encode a utf-8 message you need to use class email.mime.multipart.MIMEMultipart:

#!/usr/bin/env python3

import smtplib
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr

def send_mail(sender, to, subject, body, textType, user, password, host, port, use_tls):
    """Send an outgoing email with the given parameters."""

    # Prepare Message
    msg = MIMEMultipart()

    name, addr = parseaddr(sender)
    name = Header(name, 'utf-8').encode()
    sender = formataddr((name, addr))
    msg['From'] = sender

    name, addr = parseaddr(to)
    name = Header(name, 'utf-8').encode()
    to = formataddr((name, addr))
    msg['To'] = to

    msg['Subject'] = Header(subject, 'utf-8').encode()

    # Attach the body
    msg.attach(MIMEText(body, textType, 'utf-8'))

    with smtplib.SMTP(host, port) as server:
        if use_tls:
            server.starttls()

        server.login(user, password)  # if required
        server.send_message(msg)

if __name__ == '__main__':
    send_mail("Booboo 磨坊主 <[email protected]>",
              "Booboo 磨坊主 <[email protected]>",
              "Tést 磨坊主 with unicodé",
              'Tést 磨坊主 <span style="color:red">Message</span>',
              'html',
              'some user',
              'some password',
              'some smtp server',
              587,
              True
    )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.