Search code examples
pythonpython-3.xemailsmtpsmtplib

Python: smtplib incorrectly formats email when inside of a function, but it correctly formats it when the same code is run from outside of a function


For a reason which escapes me, whenever I try to send this email by calling a function, the email lacks a subject, lacks a "To:", and the email's body is offset by four spaces. However, when exactly the same code is run from directly within the body of a script, everything is formatted perfectly.

import smtplib

def send_email():
    sender = 'email@address.com'
    receivers = ['email@address.com']

    message_header = """From: Me <email@address.com>
    To: Me <email@address.com>
    Subject: This is the messages's subject

    """

    message_body = 'This is the message\'s body'

    message = message_header + message_body

    try:
        smtpObj = smtplib.SMTP('server_name', 25)
        smtpObj.sendmail(sender, receivers, message)
        print("Successfully sent email")
    except OSError:
        print("Error: unable to send email")

send_email()

Solution

  • (This shows you how to fix your current code; my other answer shows you the right way to implement this.)


    Each line in message_header but the first is preceded by 4 spaces, including the body after you concatenate the two strings:

    >>> repr(message_header)
    '"From: Me <email@address.com>\\n    To: Me <email@address.com>\\n    Subject: This is the messages\'s subject\\n\\n    "'
    

    (I tested this in a UNIX environment, hence the \n shown here. I assume you are using a file with DOS line endings, which already uses \r\n that SMTP expects.)

    Either eliminate them yourself:

    def send_email():
        sender = 'email@address.com'
        receivers = ['email@address.com']
    
        message_header = """From: Me <email@address.com>
    To: Me <email@address.com>
    Subject: This is the messages's subject
    
    """
    

    which is ugly, or use textwrap.dedent:

    def send_email():
        sender = 'email@address.com'
        receivers = ['email@address.com']
    
        message_header = textwrap.dedent(
            """From: Me <email@address.com>
               To: Me <email@address.com>
               Subject: This is the messages's subject
    
            """)
    

    This removes the same amount of whitespace from each line in its argument:

    dedent(text)
        Remove any common leading whitespace from every line in `text`.
    
        This can be used to make triple-quoted strings line up with the left
        edge of the display, while still presenting them in the source code
        in indented form.
    
        Note that tabs and spaces are both treated as whitespace, but they
        are not equal: the lines "  hello" and "    hello" are
        considered to have no common leading whitespace.  (This behaviour is
        new in Python 2.5; older versions of this module incorrectly
        expanded tabs before searching for common leading whitespace.)
    

    Even better, use '\r\n'.join to construct the string for you.

    def send_email():
        sender = 'email@address.com'
        receivers = ['email@address.com']
    
        headers = [
          'From: Me <email@address.com>',
          'To: Me <email@address.com>',
          "Subject: This is the message's subject"
        ]
    
        message_body = 'This is the message\'s body'
    
        message = '\r\n'.join(headers + ['', ''] + [message_body])