Check for compilation error in Excel via COM - python-3.x

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

Related

win32com.client Python - Excel task remains running after telling application to quit

I've seen a couple questions similar to this, but they both appear to involve VBA and not Python.
This is a relatively recent error, so I suspect it might have something to do with the fact that I'm using Python 3.7 now.
Basically, using the Dispatch method from win32com.client, I am able to open a new Excel workbook and make my edits as I always have been. However, for some reason, I am unable to tell the application to quit successfully.
Used to be that I could write:
self.excel_app.Quit()
But now, I'm getting an AttributeError, of all things. Saying the Excel.Application does not have a Quit() attribute. Again, this is Python 3.7. What happened?
[EDIT]
Relevant code:
import sys
from win32com.client import Dispatch
#...
class X(object):
def __init__(self):
#...
self.excel_app = Dispatch("Excel.Application")
self.report_workbook = self.excel_app.Workbooks.Add()
#...
def close_excel(self):
try:
self.excel_app.Quit()
except Exception as ex:
sys.stdout.write("Could not quit application.\n-> ({}) {}\n".format(ex.__class__.__name__, ex))
self.excel_app = None
The exception printed to the terminal is:
Could not quit application.
(AttributeError) Excel.Application.Quit
Turns out that my error was coming from the fact that I was still using threading.Thread as the base class for the object trying to do this. Apparently it doesn't work so well with the Dispatch Excel objects anymore, and I'm trying to move away from Threads anyway.

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.

Python Error Handling while base decoding

I tried to do the following :
I have read a code in base64 via QR Code and then I converted it.
If I get an error while I do the convert, I will write a error variable to 1 and then continue without exiting the program.
I don't find a solution for me. Did anyone has an idea how I can handle it?
I tried it with the Python Try Command but I didn't get it working or I have done something wrong.
here is a snip of my code:
secure = base64.b64decode(secure_base).decode("utf-8", "ignore")
number = base64.b64decode(number_base).decode("utf-8", "ignore")
start = int(base64.b64decode(start_base).decode("utf-8", "ignore"))
end = int(base64.b64decode(end_base).decode("utf-8", "ignore"))
thanks a lot.
You can use the try and Except in python in the following manner.
try:
"""some intelligent program here, which some times may FOOBAR"""
except Exception as e:
error_recieved = e
"""Do whatever you want here incase of an error"""
Remember that the program in try skips to except just after the line in which the error/exception occured.

Error handling netCDF file in Python

I am extracting data from netCDF files with Python code. I need to check if the netCDF files are in agreement with the CORDEX standards (CORDEX is a coordinated effort to carry modelling experiments with regional climate models). For this I need to access an attribute of the netCDF file. If the attribute is not found, then the code should go to the next file.
A snipet of my code is as follows:
import netCDF4
cdf_dataset = netCDF4.Dataset(file_2read)
try:
cdf_domain = cdf_dataset.CORDEX_domain
print(cdf_domain)
except:
print('No CORDEX domain found. Will exit')
....some more code....
When the attribute "CORDEX_domain" is available everything is fine. If the attribute is not available then the following exception is raised.
AttributeError: NetCDF: Attribute not found
This is a third party exception, which I would say should be handled as a general one, but it is not, as I am not able to get my "print" inside the "except" statement to work or anything else for that matter. Can anyone point me the way to handle this? Thanks.
There is no need for a try/except block; netCDF4.Dataset has a method ncattrs which returns all global attributes, you can test if the required attribute is in there. For example:
if 'CORDEX_domain' in cdf_dataset.ncattrs():
do_something()
You can do the same to test if (for example) a required variable is present:
if 'some_var_name' in cdf_dataset.variables:
do_something_else()
p.s.: "catch alls" are usually a bad idea..., e.g. Python: about catching ANY exception
EDIT:
You can do the same for variable attributes, e.g.:
var = cdf_dataset.variables['some_var_name']
if 'some_attribute' in var.ncattrs():
do_something_completely_else()

Why am I suddenly getting a no attribute 'CLSIDToPackageMap' error with win32com.client?

The code
import win32com.client as win32
Excel = win32.gencache.EnsureDispatch('Excel.Application')
used to work, but now it produces the error:
AttributeError: 'module' object has no attribute 'CLSIDToPackageMap'
what's going on?
After deleting C:\Temp\gen_py, the code above works again. Hope it can save trouble!
The main reason for this attribute error is because your COM-server has shifted from late-binding (dynamic) to early binding (static).
In Late Binding, whenever a method is called, the object is queried for the method and if it succeeds, then the call can be made.
In Early Binding, the information of the object model is determined in advance from type information supplied by the object call. Early binding makes use of MakePy. Also, early binding is case sensitive.
There are two ways to fix this issue:
Use the dynamic module to force your code to work in a late-bound oriented way. Example use:
"win32com.client.Dispatch()" instead of "win32.gencache.EnsureDispatch('Excel.Application')"
Use camelcase sensitive keywords for the early bound oriented way. Example use:
"excel.Visible()" instead of "excel.VISIBLE()" or "excel.visible()"
I guess, the code works for the first run after deleting gen_py folder but from the second run throws an error as win32.gencache.EnsureDispatch being an early binding Dispatch, gen_py folder will be created again.
I found a more elegant solution on a Github discussion and incorporated it into a function. Worked for me.
def dispatch(app_name:str):
try:
from win32com import client
app = client.gencache.EnsureDispatch(app_name)
except AttributeError:
# Corner case dependencies.
import os
import re
import sys
import shutil
# Remove cache and try again.
MODULE_LIST = [m.__name__ for m in sys.modules.values()]
for module in MODULE_LIST:
if re.match(r'win32com\.gen_py\..+', module):
del sys.modules[module]
shutil.rmtree(os.path.join(os.environ.get('LOCALAPPDATA'), 'Temp', 'gen_py'))
from win32com import client
app = client.gencache.EnsureDispatch(app_name)
return app
Thanks - worked like a charm after adding your function and making this change:
#excel = win32.gencache.EnsureDispatch('Excel.Application')
excel = dispatch('Excel.Application')
Sincerely,
js

Resources