Code executes sometimes and gives error for the other times - excel

I am trying to automate a process in excel. I have called two macros from Python. The code executes successfully sometimes and sometimes gives an error. I have saved the macros and the python file in the same location.
import os,os.path
import win32com.client
Path = 'xyz'
if os.path.exists(r'D:\Code\ABC.xlsx'):
xl = win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Open(os.path.abspath('D:\Code\ABC.xlsx'))
xl.Application.Run(Macro.xlsm!Module1.Macro1, Path)
xl.Application.Run(Macro.xlsm!Module2.Macro2)
xl.Application.ActiveWorkbook.Save()
del xl
The error that it gives at times is:
(-2147352567, 'Exception occured.',(0,''Microsoft Excel','Cannot run
macro 'D:\Code\Macro.xlsm!Module1.Macro1'. The macro may not be
available in this workbook or all macros may be disabled',
'xlmain11.chm, 0, -2146827284'), None)
I am getting a new error, which is constant:
//winp-oaf-118/FldrRedir_3$/Data/Macro.xlsm' cannot be found.Check your spelling, or try a different path.

Related

Python win32com excel process file access or permissions issue

I'm trying to resolve a permissions/file access issue with a python win32com excel process run from a flask app.
I have a python script that runs a VBA macro that works locally on the flask app server when I invoke it manually, but throws an error when I hook it up to an endpoint and query it externally through HTTP.
The script is a simple test case. It opens excel and runs a VBA macro that writes a text file with a timestamp in the same directory as the macro workbook.
def testXL():
try:
import pythoncom
pythoncom.CoInitialize()
fn = 'vbRender.xlsm'
fp = os.path.join(cc.appRootDir,"workingProductDir",fn)
xl=win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Open(os.path.abspath(fp), ReadOnly=1)
xl.Application.Run("%s!DebugEnvironment.writeFileTest"%(fn))
del xl
return "Success"
except:
excInfo = sys.exc_info()
excDetails = ''.join(traceback.format_exception(*excInfo))
return "%s: %s"%(excInfo,excDetails)
This runs successfully on my flask app server when I invoke it from command line or IDLE.
When I hook it up to a flask endpoint like this:
#app.route('/dev/testxl')
def testXL():
...
and then hit the endpoint from another machine I get the following error:
(com_error(-2147352567, 'Exception occurred.', (0, 'Microsoft Excel', "Microsoft Excel cannot access the file 'C:\\...\\workingProductDir\\vbRender.xlsm'. There are several possible reasons:\n\n• The file name or path does not exist.\n• The file is being used by another program.\n• The workbook you are trying to save has the same name as a currently open workbook.", 'xlmain11.chm', 0, -2146827284), None), ): Traceback (most recent call last): File "C:\...\flaskAppSocket.py", line 1372, in testXL xl.Workbooks.Open(os.path.abspath(fp), ReadOnly=1) File ">", line 5, in Open pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, 'Microsoft Excel', "Microsoft Excel cannot access the file 'C:\\...\\workingProductDir\\vbRender.xlsm'. There are several possible reasons:\n\n• The file name or path does not exist.\n• The file is being used by another program.\n• The workbook you are trying to save has the same name as a currently open workbook.", 'xlmain11.chm', 0, -2146827284), None)
It doesn't appear to be a concurrent use issue, since I can boot up an instance of IDLE on the flask server machine and run the script locally between attempts to access the endpoint via HTTP. It is always successful when run manually on the server.
It looked to me like a permissions issue, but my flask app is very clearly running with highest level privileges. To confirm, I created an endpoint that just writes a file to the same folder using python, instead of an excel VBA process. The following endpoint works:
#app.route('/dev/testfilewrite')
def testWriteFile():
n = datetime.datetime.now()
fp = os.path.join(cc.appRootDir,"workingProductDir",
"%s log test.txt"%(n.strftime("%Y-%m-%d-%H-%M-%S")))
f = open(fp, "w")
f.write("this is a test")
return "Success"
So it has something to do with that particular file or use.
After restarting my flask app machine and trying again with no resolution, I am at a loss as to how to proceed. The exception printout clearly shows that it is failing before it gets to the macro, in the process of opening the file. How can I troubleshoot this further?

Check for compilation error in Excel via COM

I am connected to an Excel application and can execute the "Debug"->"Compile VBAProject" from my Python code using win32com like so (inspired by code from here):
from win32com import client
def compile(self):
self.__excel = client.GetActiveObject("Excel.Application")
compile_button = self.__excel.VBE.CommandBars.FindControl(1, 578)
compile_button.Execute()
If there is a compilation error in the Excel VBA code I get a popup message in Excel telling me the error just fine.
Now I would like to check from the Python code if there was a compilation error and raise an exception if there was. I don't necessarily need the compilation error to be part of the exception but if that were possible I would of course gladly take that, too.
Can this be done somehow?
I've been experimenting with all kinds of window counts before and after the compilation etc. but so far have not found a property of any object that would indicate that there was a popup or a compilation error.
Ok, I found a somewhat ugly but doable way - that I would like to document for others having the same issue:
You need to import a code file into the opened Excel file that has (at least) one function defined. Then you can call this function from your Python code and catch any exception. If there was an exception your code - including the imported file - did not compile, if there is none the compilation was pass.
Here's my code:
compile_code.bas
Public Sub compileCode()
' doesn't need to do anything, it just needs to be available!
End Sub
Python file
from win32com import client
def compile(self) -> bool:
self.__excel = client.GetActiveObject("Excel.Application")
self.__book = self.__excel.ActiveWorkbook
self.__book.VBProject.VBComponents.Import(<Path_to_compile_code.bas>)
try:
self.__excel.Application.Run("compileCode")
# if you reach here the code compiled
return True
except Exception:
return False

xlwings: Unable to import functions (UDFs) via xlwings ribbon

I'm trying to import a user-defined function(UDF) via xlwings but am encountering an issue. Upon pressing the import functions button in the xlwings ribbon, I receive the following run time error:
`Run_time error '1004
`Method of 'VBProject' of object '_Workbook' failed.'
According to the VBA debugger, the below module contains the problem:
Sub ImportXlwingsUdfsModule(tf As String)
' Fallback: This is called from Python as direct pywin32 calls were
' sometimes failing, see comments in the Python code
On Error Resume Next
ActiveWorkbook.VBProject.VBComponents.Remove
ActiveWorkbook.VBProject.VBComponents("xlwings_udfs")
On Error GoTo 0
**ActiveWorkbook.VBProject.VBComponents.Import tf**
End Sub
The .py file containing the UDF is saved in the same folder as the calling .xlsm workbook.
How do I rectify this so I can utilize UDFs?
Thanks to the xlwings team for providing a link that helped me resolve the issue.
One needs to ensure that Trust Access to VBA object model is enabled.
See: https://docs.xlwings.org/en/stable/udfs.html#one-time-excel-preparations
If you have trusted access to the VBA object model and you are getting a TypeError, there is an additional answer that applies to both versions 0.22.2 and 0.22.3 and maybe earlier.
In file Lib>Site-Packages>xlwings>udfs.py on line 651 or 652 (depending on the version) insert ".Item" where shown in the following:
xl_workbook.VBProject.VBComponents.Remove(xl_workbook.VBProject.VBComponents.Item("xlwings_udfs"))
I do not know why this works but it does. The original line does work in VBA with only modifications that apply to VBA syntax and the workbook reference. (i.e. see the VBA code in the question for an example.

Xlwings addin works but fails to update as I change underlying code

I have downloaded xlwings addin to launch some python scripts from a VBA macro. My python version is 3.7 , 64-bit and I work in a sublime environment.
I've defined:
HelloWorld24 VBA macro on a Example.xlsm workbook which runs a
working_test.py script that changes the workbook
Note that the workbook and the script are in the same folder. If I run HelloWorld24, working_test.py runs as expected and Example.xlsm is changed correspondingly. So far so good. However, if I change working_test.py, save it and run HelloWorld24 again, Example.xlsm does not change. I've cleared Example.xlsm, ran the macro again and get the same result as in the first run.
1.I don' think this is a problem with VBA as if I change the non-run python part of HelloWorld24, Example.xlsm is changed correspondingly (for example below if I change 912 to 1000 in the second line of the macro).
2.Also, if I create a new python script and a new macro, the addin works as expected as it takes into accoutn the changes in the underlying python file.
3.It seems to me that once run, the addin freezes the python script to its first version and I can't find a way to refresh it short of creating a new python file with a different name.
4.Even if I introduce errors in the working_test.py file, the VBA macro runs fine.
5.Conversely, if I create a macro on a file that has python errors, I will always receive the error message from VBA even if I correct the underlying errors in the python file.
#working_test.py
def world():
wb = xw.Book.caller()
wb.sheets[0].range('A1').value = 'Hello World!'
#VBA macro
Sub HelloWorld24()
RunPython ("import working_test; working_test.world()")
Range("B2").Value = Range("B2").Value + 912
End Sub

Attribute error while trying to refresh Pivot Tables with Python 2.7

I have tried to find the answer online but only got confused.
I am a windows user, have Python 2.7 and work with Excel 2010 files saved on sharepoint, trying to automate the data extraction. Basically, my solitaire-long program opens the files one by one, extract the data and saves them into a new xl file.
Hitherto I have used xlwt and xlrd and everything was going pretty smoothly. But now I have encountered a file xlsm that contains the pivot tables that need to be refreshed every time . I have googled it up and found the code:Python: Refresh PivotTables in worksheet
The problem is it does not work for me at all... I keep getting attribute errors like
AttributeError: .Open
I have noticed that the syntax differs substantially, too (wb.Sheets.Count versus wb.nsheets). With win32com I am not able even to iterate through the sheets of a workbook... I just do not have a clue what is a problem - the Python version, importing problem or whatever...
The thing that I cannot find in xlrd/xlwt: ws.PivotTables(j).PivotCache().Refresh() - if I am not mistaken, the problem is that with xlrd/xlwt I am not actually opening Excel file, so probably it is not possible to refresh data using them... Alas, moving to win32com.client hasn't helped...
Any suggestions or links? :) I am probably gonna add an automatic update (refresh) to a VBA code of the xl file in question, but I'd rather not change the files, but my code :)
Edit: I paste my code below, along with an error I keep getting:
the below thing, copied from someone else's code, does not work, returning an AttributeError: Property 'Excel.Application.Visible' can not be set.:
import win32com.client
import os
xl = win32com.client.DispatchEx("Excel.Application")
wb = xl.workbooks.open("//some_dir.xlsm")
xl.Visible = True
wb.RefreshAll()
xl.Quit()
Alas when I try to merge it into a program that uses also xlrd and xlwt it does not work...
from copy import deepcopy
from xlrd import open_workbook
from xlutils.copy import copy as copy
from xlwt import *
from datetime import *
#some definitions here...
rb = open_workbook('my_template.xlsx')
wb = copy(rb)
sheet_1 = wb.get_sheet(0)
sheet_2 = wb.get_sheet(1)
#now the code that evaluates the dates:
import datetime
ys = raw_input('year in yyyy format:\n')
ms = raw_input('month in mm format:\n')
ds = raw_input('day in dd format:\n')
#here the definitions concerning dates are called, all in xlwt and xlrd
#now other files are being opened and data extracted and written into sheet_1 and sheet_2
#now it's time for refreshments ;) - I wanted to 1. open and update the file with win32com, #close it and 2. with xlrd get the data from the updated file:
#refreshing all (preparing file)
import win32com.client
import os
xl = win32com.client.DispatchEx("Excel.Application")
wb = xl.workbooks.open("some_dir.xlsm") #it is a shared file saved in intranet
xl.Visible = True
wb.RefreshAll()
xl.Quit()
print 'file refreshed'
file = open_workbook("some_dir.xlsm")
row_sheet1 = 176
row_sheet2 = 248
for sheet in file.sheets():
print sheet.name
#... the rest of the code ensues
The error again is: AttributeError: Property 'Excel.Application.Visible' can not be set.
I guess I am not supposed to use xlrd and win32com.client simultanously and my whole idea is just soooo wrooong? ;) But why does it not work "alone", I mean the first, short code?

Resources