Search code examples
pythonoutlookpyinstallerwin32commailitem

Unable to read Outlook Mail Items using win32com when run as exe build using pyInstaller - Python code works


I have following code which is able to read Mail Items from outlook and download the attachment when python code is run using eclipse. however, when the same code is compiled to an exe using pyinstaller, it fails to read the mail items with error: ComObject Unknown

The following code reads email item in a folder and downloads excel attachment from mail with specific subject. then strips off the password from the downloaded xls file and saves it as xlsx file for further processing.

The code below runs fine when run in eclipse environment or when called using python.exe from command prompt. but fails to recognize email item when run using exe compiled by pyinstaller. I am using Windows 10 and outlook 2016 over exchange server what is it that i am missing here? below is the code block:

import win32com.client as wc
from datetime import date
import os
import configparser

print('Reading Config file')
config=configparser.ConfigParser()
config.sections()
config.read('ReadOutlook.config')
configuration=config['DEFAULT']
mailboxname=configuration['mailboxname']
mailfolder_to_look_for=configuration['mailfolder_to_look_for']
downloadpath=configuration['downloadpath']
ULMISPath=configuration['ULMISPath']
CFMISPath=configuration['CFMISPath']
ulmis_password=configuration['ulmis_password']
cfmis_password=configuration['cfmis_password']
ulmisSubjectcontent=configuration['ulmisSubjectcontent']
cfmisSubjectcontent=configuration['cfmisSubjectcontent']
ulmisfilenamesearch=configuration['ulmisfilenamesearch']
cfmisfilenamesearch=configuration['cfmisfilenamesearch']
print (date.today())

outlook = wc.Dispatch("Outlook.Application")
namespace = outlook.GetNamespace("MAPI")
root = namespace.Folders.Item(mailboxname)
print(root.Name)
#print(root.Name)
MyMails=root.Folders.Item(mailfolder_to_look_for)

def Remove_password_xlsx(filename, pw_str,newfilename):
    print('opening Excel Application')
    xcl = wc.Dispatch("Excel.Application")
    print('opening file:' ,filename)
    wb = xcl.Workbooks.Open(filename, False, False, None, pw_str)
    xcl.DisplayAlerts = False
    print('Removing password for',filename)
    wb.SaveAs(filename, None,'','')
    print('Now saving as xlsx',newfilename)
    wb=xcl.Workbooks.Open(filename,False,False,None)
    wb.SaveAs(newfilename, FileFormat=wc.constants.xlOpenXMLWorkbook,CreateBackup=False)
    xcl.Quit()
for mailitem in range(len(MyMails.Items),0,-1):
    print(MyMails.Items[mailitem].Subject)
    try:
        if(MyMails.Items[mailitem].ReceivedTime.date()==date.today() 
           and ((MyMails.Items[mailitem].Subject.find(ulmisSubjectcontent)!=-1 
                 and MyMails.Items[mailitem].Subject.find('With Collection')!=-1) 
                 or MyMails.Items[mailitem].Subject.find(cfmisSubjectcontent)!=-1)):
            print(MyMails.Items[mailitem].Subject)
            # if i.Attachments:
            for f in MyMails.Items[mailitem].Attachments:
                if f.FileName.find(ulmisfilenamesearch)!=-1:
                    f.SaveAsFile(downloadpath + '\\ULMIS.xls')
                    Remove_password_xlsx(downloadpath+'\\ULMIS.xls'
                                         , ulmis_password,ULMISPath)
                    print('removing ULMIS.xls')
                    os.remove(downloadpath+'\\ULMIS.xls')
                    break
                else:
                    if f.FileName.find(cfmisfilenamesearch)!=-1:
                        f.SaveAsFile(downloadpath + '\\CFMIS.xls')
                        Remove_password_xlsx(downloadpath +'\\CFMIS.xls'
                                             , cfmis_password,CFMISPath)
                        print('removing CFMIS.xls')
                        os.remove(downloadpath+'\\CFMIS.xls')
                        break
                    
    except:
        print('an error occurred')
        pass

Printing the Mail Subject gives error as:

Traceback (most recent call last): File "ReadOutlook.py", line 45, in module File >"win32com\client\dynamic.py", line 279, in getitem File >"win32com\client\util.py", line 37, in getitem File >"win32com\client\util.py", line 56, in __GetIndex IndexError: list index >out of range

IndexError: list index out of range Failed to execute script 'ReadOutlook' due to unhandled exception!


Solution

  • from the comments reced above by DS_London, i have now changed my code as follows:

    import win32com.client as wc
    from datetime import date
    import os
    import configparser
    # while creating exe on pyinstaller use "win32timezone" in hidden import section
    print('Reading Config file')
    config=configparser.ConfigParser()
    config.sections()
    config.read('ReadOutlook.config')
    configuration=config['DEFAULT']
    mailboxname=configuration['mailboxname']
    mailfolder_to_look_for=configuration['mailfolder_to_look_for']
    downloadpath=configuration['downloadpath']
    ULMISPath=configuration['ULMISPath']
    CFMISPath=configuration['CFMISPath']
    ulmis_password=configuration['ulmis_password']
    cfmis_password=configuration['cfmis_password']
    ulmisSubjectcontent=configuration['ulmisSubjectcontent']
    cfmisSubjectcontent=configuration['cfmisSubjectcontent']
    ulmisfilenamesearch=configuration['ulmisfilenamesearch']
    cfmisfilenamesearch=configuration['cfmisfilenamesearch']
    print (date.today())
    
    # outlook = wc.Dispatch("Outlook.Application")
    outlook = wc.gencache.EnsureDispatch("Outlook.Application")
    namespace = outlook.GetNamespace("MAPI")
    root = namespace.Folders.Item(mailboxname)
    print(root.Name)
    #print(root.Name)
    MyMails=root.Folders.Item(mailfolder_to_look_for)
    
    def Remove_password_xlsx(filename, pw_str,newfilename):
        print('opening Excel Application')
        xcl=wc.gencache.EnsureDispatch("Excel.Application")
        # xcl = wc.Dispatch("Excel.Application")
        print('opening file:' ,filename)
        wb = xcl.Workbooks.Open(filename, False, False, None, pw_str)
        xcl.DisplayAlerts = False
        print('Removing password for',filename)
        wb.SaveAs(filename, None,'','')
        print('Now saving as xlsx',newfilename)
        wb=xcl.Workbooks.Open(filename,False,False,None)
        wb.SaveAs(newfilename, FileFormat=wc.constants.xlOpenXMLWorkbook,CreateBackup=False)
        xcl.Quit()
    for mailitem in range(len(MyMails.Items),0,-1):
        # print(MyMails.Items[mailitem].ReceivedTime.date())
        try:
            if(MyMails.Items[mailitem].ReceivedTime.date()==date.today() 
               and ((MyMails.Items[mailitem].Subject.find(ulmisSubjectcontent)!=-1 
                     and MyMails.Items[mailitem].Subject.find('With Collection')!=-1) 
                     or MyMails.Items[mailitem].Subject.find(cfmisSubjectcontent)!=-1)):
                print(MyMails.Items[mailitem].Subject)
                # if i.Attachments:
                for f in MyMails.Items[mailitem].Attachments:
                    if f.FileName.find(ulmisfilenamesearch)!=-1:
                        f.SaveAsFile(downloadpath + '\\ULMIS.xls')
                        Remove_password_xlsx(downloadpath+'\\ULMIS.xls'
                                             , ulmis_password,ULMISPath)
                        print('removing ULMIS.xls')
                        os.remove(downloadpath+'\\ULMIS.xls')
                        break
                    else:
                        if f.FileName.find(cfmisfilenamesearch)!=-1:
                            f.SaveAsFile(downloadpath + '\\CFMIS.xls')
                            Remove_password_xlsx(downloadpath +'\\CFMIS.xls'
                                                 , cfmis_password,CFMISPath)
                            print('removing CFMIS.xls')
                            os.remove(downloadpath+'\\CFMIS.xls')
                            break
                        
        except:
            # print('an error occurred')
            pass
    

    after this, I was getting error of missing import for win32timezone, which I corrected by adding hidden_import parameter in pyInstaller while building the exe - followed this post for it: ImportError: No module named win32timezone when i make a singleone exe from a python script with pyInstaller