Search code examples
pythonemailjinja2smtplib

Jinja2 template rendering duplicate body / SMTP email sending twice


I created a relatively simple emailer class in python with the intention of reading in a CSV file and filling in a Jinja2 template with dynamic information from the CSV and then sending out emails for every row in the CSV.

I was playing around with getting it set up and I think I finally worked out all the Godaddy / Hosting quirks and am connected to the SMTP relay and can send emails.

When I sent my first email I noticed I was getting an exact duplicate copy when running the script with only "# temp.send(temp.msg("[email protected]", "Recipient"))" being actually sent (commented out in my code as I continued to debug). Something to note is that I did not properly end the connection after I was finished sending the email, I fixed that now but Godaddy has 5 email per day limit for relays on the hosting we have so I can't test exact same configuration.

Regardless, my Jinja2 template with code as is, is rendering two identical messages (both html pages no weird spacing at the end just a newline from the closing tag of the previous HTML, newline, identical opening tag) here is console output of the ending and beginning of the duplicate.

    </div>
  </div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>

The dynamic content is being filled in properly on both console outputs and the content when sent to a test email looked proper besides some poor formatting in the HTML (dynamic content was filled, header info correct).

My questions are:

  1. Is the duplicate Jinja render message causing the duplicate message? (Is this possible, is this behavior normal etc)
  2. Could something in "email-template.html" be causing the duplication? I didn't provide the template because it has identifying information in it but I can redact and supply it if needed.

I searched for this problem found a StackOverflow post with no answer: Sending email with Python and smtplib getting two duplicate emails

I don't know if this person debugged their template to see what it was rendering though.

from jinja2 import *;
import os
from os.path import join, dirname
from dotenv import load_dotenv
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

#Adding .env to path
dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)


#Emailer Class that loads in templates and config, and handles data parsing.
class Emailer:
    #Load template and set subject
    env = Environment(
        loader=PackageLoader('email-lib')
    )
    template = env.get_template("email-template.html")
    subject = os.environ.get("SUBJECT")
    # Mail can't be sent while not connected.
    connected = False

    #Init
    def __init__(self):
        self.ourEmail = os.environ.get("EMAIL")
        self.password = os.environ.get("PASS")
        self.server = smtplib.SMTP(os.environ.get("SERVER"))

    #Testing Render
    def print(self, name):
        print(self.template.render(recipientName = name))

    #Returning render without needing to get acess to public template
    def render(self, name):
        return self.template.render(recipientName = name)
    
    #Generating plain text body with desired content
    def msg(self, toEmail, recipient):
        msg = MIMEMultipart('alternative')
        msg["From"] = self.ourEmail
        msg["To"] = toEmail
        msg["Subject"] = self.subject
        # msg.attach(MIMEText(self.render(recipient),'plain'))
        msg.attach(MIMEText(self.render(recipient), 'html'))
        return msg
    
    #Connect and login to SMTP server
    def startConnect(self):
        self.connected = True
        self.server.starttls()
        self.server.login(self.ourEmail,self.password)

    #Send to the smtp server
    def send(self, msg):
        if self.connected:
            self.server.sendmail(msg["From"], msg["To"], msg.as_string())
        else:
            raise ConnectionAbortedError("Emailer is not connected to an email server but a message attempted to send.")
            
    #Stop server connection when finished.  
    def stop(self):
        self.connected = False
        self.server.quit()
temp = Emailer()
# print(temp.msg("[email protected]", "Recipient"))

print(temp.template.render(recipientName = "Test"))


# temp.startConnect()

# temp.send(temp.msg("[email protected]", "Recipient"))

# temp.stop()

Solution

  • It was an issue with the imports and leaving code around outside main.

    Wrapping the execution portion in a main function fixed it:

    if __name__ == '__main__':
        #Adding .env to path
        dotenv_path = join(dirname(__file__), '.env')
        load_dotenv(dotenv_path)
        temp = Emailer()
        # print(temp.msg("[email protected]", "Recipient"))
       temp.startConnect()
    
       temp.send(temp.msg("[email protected]", "test"))
    
       temp.stop()