Search code examples
pythonemailsmtpemail-attachments

Getting a KeyError when attaching attachments to an email


So I'm trying to create an automated email script that reads surnames, recipient emails, and files to be attached from an excel file.

For some reason, when I send the email with the attachments it returns a KeyError, which I think traces back to the placeholder for the filename of the attachment that I'm using .add_header('Content-Disposition', 'attachment; filename = {file}') with. Because whenever I change the placeholder, the KeyError specifies what I wrote in it.

Anyways, here's the source code

import pandas as pd
import smtplib
import ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders

context = ssl.create_default_context()

sender = '[email protected]'
sender_pass = '123456789'
email_subject = 'subject'
attachments_list = 'testingemail.xlsx'

body = 'Dear Mx. {surname} body'
    
def attach_attachment(attachment_list, payload):

    # opening excel file into dataframe
    attachments = pd.read_excel(attachment_list, sheet_name = 'attachments')

    for file in attachments['attachments']:
        with open(file, 'rb') as attachment:
            part = MIMEBase('application', 'octet-stream')
            part.set_payload((attachment).read())

        encoders.encode_base64(part) # encoding attachment
        part.add_header(
            'Content-Disposition',
            'attachment; filename = {file}',
        )          

        # attaching attachment
        payload.attach(part)      

def composing_email(e_sender, e_password, e_subject, e_recipient, attachment_list):
    # setup the MIME
    message = MIMEMultipart()
    message['From'] = e_sender
    message['To'] = e_recipient
    message['Subject'] = e_subject

    # attaching body and the attachments for the mail
    message.attach(MIMEText(body, 'plain'))
    attach_attachment(attachment_list, message)
    
    # create SMTP session for sending the mail
    session = smtplib.SMTP('smtp.gmail.com', 587) # gmail port
    session.starttls(context = context) # securing connection
    session.login(e_sender, e_password) # logging into account

    text = message.as_string()

    # actually sending shit
    session.sendmail(e_sender, 
                    e_recipient, 
                    text.format(surname = surname, 
                                receiver_email = recipient, 
                                sender = e_sender,
                                )
                    )

    session.quit()
    print('Mail successfully sent to {surname} with emal {recipient}')
   

if __name__ == '__main__':
    surname_email = pd.read_excel('testingemail.xlsx', sheet_name = 'surname_email')
    for surname, recipient in surname_email.itertuples(index=False):
        composing_email(sender, sender_pass, email_subject, recipient, attachments_list)
  

and here's the error

Traceback (most recent call last):
  File "c:\Users\zddj9\Desktop\auto_email\script.py", line 77, in <module>
    composing_email(sender, sender_pass, email_subject, recipient, attachments_list)
  File "c:\Users\zddj9\Desktop\auto_email\script.py", line 64, in composing_email
    text.format(surname = surname,
KeyError: 'file'

Solution

  • I just realized that I needed to use f-string formatting

    instead of this

    part.add_header(
                'Content-Disposition',
                'attachment; filename = {file}',
            )          
    

    add an f before the argument with the placeholder

    part.add_header(
                'Content-Disposition',
                f'attachment; filename = {file}',
            )          
    

    The confirmation also wouldn't work without f-string formatting

    print('Mail successfully sent to {surname} with email {recipient}')
    

    It should be this

    print(f'Mail successfully sent to {surname} with email {recipient}')