Comtypes has no attribute '_Application' - python

I'm experiencing a weird bug in a file conversion script I wrote in python. I'm using the comtypes library to convert different types of files into pdfs and for some reason, I get module 'comtypes.gen.Excel' has no attribute '_Application' whenever I try to create a client object for an excel application. I can't seem to find anything online specific to this issue. The script was working fine about a month or two ago, so I'm confused as to why it isn't working anymore - the only thing I could think of was excel updating or something (if that would even matter). I have the office 2016 if that's relevant. If anyone has experienced this bug or has any ideas, help would be greatly appreciated. Here's the script, for reference:
import comtypes.client
excel = comtypes.client.CreateObject("Excel.Application") # exception here
excel.Visible = False
in_file = "INPUT_FILE"
out_file = "OUT_FILE"
f = excel.Workbooks.Open(in_file)
f.ExportAsFixedFormat(0, out_file, 1, 0)
f.Close()
excel.Close()

Instead of using comtypes.client
Use pywin32
import win32com.client
#Replace excel=comtype.client.blablabla to
excel=win32com.client.DispatchEx("Excel.Application")
Keep remaining things as it is.
Ref. https://github.com/shardulind/pdfconv_python/blob/master/convertor.py

Related

Python xlwings "Unable to get repr for <class 'xlwings.main.Book'>" when reading workbook

 I´m trying to update a worksheet named table by passing a dataframe to it. I wrote the code in VSCode and it works fine, but when I have to run it on the company´s PC using PyCharm I get the error "Unable to get repr for <class 'xlwings.main.Book'>".
This is the part of the code that´s giving me an error:
import xlwings as xw
with xw.App(visible=False) as app:
app.display_alerts = False
wb = app.books.open(myfilepath, update_links=False) #HERE
ws = wb.sheets[sheet_name] #Consequently, from this point on nothing works...
ws.tables["Tabelle1"].update(dataframe, index=False)
wb.save(myfilepath)
wb.close()
Does someone know what could the problem be? I have looked all over the internet but found nothing helpful.
The current answer to a similar question on SO is "If the code works fine without it, then it is nothing to worry about.". In my case the code does not work properly.

Could anyone give a simple but generally useful template for python code to be embedded into LibreOffice Calc?

Update acknowledging comment 1 and 4:
Indentation corrected, does compile in Geany, obvious redundancies stripped.
The first question is: why am I getting the args error when I try to run my code? The error message refers to a LO file.... (linux) /opt/libreoffice7.2/programs/pythonscript.py # line 915.
An excerpt from that file says...
def invoke(self, args, out, outindex ): # 910
log.debug( "PythonScript.invoke " + str( args ) ) # 911
try: # 912
if (self.args): # 913
args += self.args # 915
ret = self.func( *args ) # 915
My code is...
# -*- coding: UTF-8 -*-
from __future__ import unicode_literals
# Gets the current document
doc = XSCRIPTCONTEXT.getDocument()
ctx = XSCRIPTCONTEXT.getComponentContext()
sm = CTX.ServiceManager
import time
import datetime
import serial
import openpyxl
def PySerLO (args=None):
from datetime import date
from openpyxl import load_workbook
ser = serial.Serial('/dev/ttyUSB0') # ""ls /dev/ttyUSB* -l"" if unsure.
# Load the workbook and select the sheet...
wb = load_workbook('/media/mpa/UserFiles/LibreOffice/mpaNEW-spreadsheet.xlsx')
sheet = wb['Sheet1']
headings = ("Date", "Time", "Raw Data")
sheet.append(headings)
try:
while True:
# Get current date, Get current time, Read the serial port
today = date.today()
now = datetime.datetime.now().time()
data = ser.readline().decode()
if data != "":
row = (today, now, float((data))) # For some reason the first data point is garbage so don't do anything with this
row = (today, ("%s"%now), float((data)))
sheet.append(row)
ser.flush()
#Save the workbook to preserve data
wb.save('/media/mpa/UserFiles/LibreOffice/mpaNEW-spreadsheet.xlsx')
finally:
# Make sure the workbook is saved at end
wb.save('/media/mpa/UserFiles/LibreOffice/mpaNEW-spreadsheet.xlsx')
print('Data Finished')
I get this error on executing a macro executing command button...
I appreciate your patience and suggestions on improvement thus far.
The ultimate aim of this is to track and instantly plot scientific data, introduce my students to the power of LibreOffice - They download LO and I just pass them the fully contained spreadsheet. They then connect the hardware and they have an excellent resource on LO forever. Almost none of them have ever heard of LibreOffice (or Linux either!)
First of all, I do not think you should start by embedding the macro in the document. I don't think it is required for the reasons you seem to believe. Instead, put your code in My Macros (the LibreOffice user directory) where it is easier to develop. Then call it from a button or by going to Tools > Macros > Run Macro - no need to use APSO as it seems like that is causing you even more confusion. The only reason for embedding is for convenience, to make it easier to keep the document and a bit of code together. For complex code that needs to be given to multiple people, it's generally better to create a full-fledged extension.
Second, it looks like the error may be from M1.py line 22 - is this your code? I cannot tell where line 22 might be or where exactly the error is coming from. Most of us reading your question are looking for the code where the error message comes from, but we cannot find it because you did not provide the relevant file name or line number of your code. It is your job to simplify your code enough so that you can see where the error is coming from, and then give that information in the question.
The error mentions create_instance which is the call for creating an UNO service.
Also I am not comfortable with the idea of storing XSCRIPTCONTEXT values in global variables unless you're sure you know what you're doing (and it's pretty clear you don't. :) )
Also I have strong doubts about achieving success by combining different libraries - it looks like you want to use both openpyxl and the UNO interface in the same code, and I would not expect that to turn out well. Just stick with plain UNO because there are plenty of people who can help with that. (Or if you choose openpyxl then I will ignore your question and someone knowledgeable about that may be able to help.)
Finally, it seems like import uno is missing. That alone could explain the error, and it's hard to imagine how you have all this code yet didn't even start with that.
Before trying all of this complex code, get a simple example working. It looks like there are a lot of python-uno fundamentals you still need to figure out first, and it doesn't look like you have put much effort into working through a tutorial yet.

Python - "KeyError : System.Object" - Pyadomd - Querying a SSAS Data Source

Working on a project where I am trying to query a SSAS data source we have at work through Python. The connection is presently within Excel files, but I am trying to reverse engineer the process with Python to automate part of the analysis I do on a day to day... I use the pyadomd library to connect to the data source, here`s my code:
clr.AddReference(r"C:\Program Files (x86)\Microsoft Office\root\vfs\ProgramFilesX86\Microsoft.NET\ADOMD.NET\130\Microsoft.AnalysisServices.AdomdClient.dll")
clr.AddReference('Microsoft.AnalysisServices.AdomdClient')
from Microsoft.AnalysisServices.AdomdClient import AdomdConnection , AdomdDataAdapter
from sys import path
path.append('C:\Program Files (x86)\Microsoft Office\root\vfs\ProgramFilesX86\Microsoft.NET\ADOMD.NET\130\Microsoft.AnalysisServices.AdomdClient.dll')
import pyadomd
from pyadomd import Pyadomd
from pyadomd._type_code import adomd_type_map, convert
constr= "connection string"
with Pyadomd(constr) as conn:
with conn.cursor().execute(query) as cur:
print(cur.fetchall())
Which works (in part), seemingly I am able to connect to the SSAS data source. Say I do conn = Pyadomd(constr), it returns no error (no more as it did before). The issue is when I try to execute the query with the cursor it returns an error saying:
File "C:\Users\User\Anaconda3\lib\site-packages\pyadomd\pyadomd.py", line 71, in execute
adomd_type_map[self._reader.GetFieldType(i).ToString()].type_name
KeyError: 'System.Object'
By doing a bit of research, I found that KeyError meant that the code was trying to access a key within a dictionary in which that key isn't present. By digging through my variables and going through the code, I realized that the line:
from pyadomd._type_code import adomd_type_map
Created this dictionary of keys:values:
See dictionary here
Containing these keys: System.Boolean, System.DateTime, System.Decimal, System.Double, System.Int64, System.String. I figured that the "KeyError: System.Object" was referring to that dictionary. My issue is how can I import this System.Object key to that dictionary? From which library/module/IronPython Clr reference can I get it from?
What I tried:
clr.AddReference("System.Object")
Gave me error message saying "Unable to find assembly 'System.Object'. at Python.Runtime.CLRModule.AddReference(String name)"
I also tried:
from System import Object #no error but didn't work
from System import System.Object #error saying invalid syntax
I think it has to do with some clr.AddReference IronPython thing that I am missing, but I've been looking everywhere and can't find it.
Thanks!
Glad that the newer version solved the problem.
A few comments to the code snippet above. It can be done a bit more concise 😊
Pyadomd will import the necessary classes from the AdomdClient, which means that the following lines can be left out.
clr.AddReference(r"C:\Program Files (x86)\MicrosoftOffice\root\vfs\ProgramFilesX86\Microsoft.NET\ADOMD.NET\130\Microsoft.AnalysisServices.AdomdClient.dll")
clr.AddReference('Microsoft.AnalysisServices.AdomdClient')
from Microsoft.AnalysisServices.AdomdClient import AdomdConnection , AdomdDataAdapter
Your code will then look like this:
import pandas as pd
from sys import path
path.append(r'C:\Program Files (x86)\MicrosoftOffice\root\vfs\ProgramFilesX86\Microsoft.NET\ADOMD.NET\130')
from pyadomd import Pyadomd
constr= "constring"
query = "query"
with Pyadomd(constr) as con:
with con.cursor().execute(query) as cur:
DF = pd.DataFrame(cur.fetchone(), columns = [i.name for i in cur.description])
The most important thing is to add the AdomdClient.dll to your path before importing the pyadomd package.
Furthermore, the package is mainly meant to be used with CPython version 3.6 and 3.7.
Well big problems require big solutions..
After endlessly searching the web, I went on https://pypi.org/project/pyadomd/ and directly contacted the author of the package (SCOUT). Emailed him the same question and apparently there was a bug within the code that he fixed overnight and produced a new version of the package, going from 0.0.5 to 0.0.6. In his words:
[Hi,
Thanks for writing me 😊
I investigated the error, and you are correct, the type map doesn’t support converting System.Object.
That is a bug!
I have uploaded a new version of the Pyadomd package to Pypi which should fix the bug – Pyadomd will now just pass a System.Object type through as a .net object. Because Pyadomd doesn’t know the specifics of the System.Object type at runtime, you will then be responsible yourself to convert to a python type if necessary.
Please install the new version using pip.]1
So after running a little pip install pyadomd --upgrade, I restarted Spyder and retried the code and it now works and I can query my SSAS cube !! So hopefully it can help others.
Snippet of the code:
import pandas as pd
import clr
clr.AddReference(r"C:\Program Files (x86)\MicrosoftOffice\root\vfs\ProgramFilesX86\Microsoft.NET\ADOMD.NET\130\Microsoft.AnalysisServices.AdomdClient.dll")
clr.AddReference('Microsoft.AnalysisServices.AdomdClient')
from Microsoft.AnalysisServices.AdomdClient import AdomdConnection , AdomdDataAdapter
from sys import path
path.append(r'C:\Program Files (x86)\MicrosoftOffice\root\vfs\ProgramFilesX86\Microsoft.NET\ADOMD.NET\130\Microsoft.AnalysisServices.Ado mdClient.dll')
import pyadomd
from pyadomd import Pyadomd
constr= "constring"
query = "query"
and then as indicated on his package website:
with Pyadomd(constr) as con:
with con.cursor().execute(query) as cur:
DF = pd.DataFrame(cur.fetchone(), columns = [i.name for i in cur.description])
and bam! 10795 rows by 39 columns DataFrame, I haven't precisely calculated time yet, but looking good so far considering the amount of data.

while working with gspread-pandas module, I want to change default_dir of the module

import json
from os import path, makedirs
_default_dir = path.expanduser('~/.config/gspread_pandas')
_default_file = 'google_secret.json'
def ensure_path(pth):
if not path.exists(pth):
makedirs(pth)
hi, I'm currently working on data collection via selenium and pandas to parse the data and edit it with pandas to send the data to google spread
however, while I'm working on gspread-pandas module, the module needs to put google_secret json file to '~/.config/gspread_pandas'. which is fixed location as described in the link below
https://pypi.python.org/pypi/gspread-pandas/0.15.1
I want to make some function to set the custom location to achieve independent working app environment.
for example, I want to locate the file to here
default_folder = os.getcwd()
the default_folder will be where my project is located(the same folder)
what can I do with it?
If you see the source https://github.com/aiguofer/gspread-pandas/blob/master/gspread_pandas/conf.py you can notice, that you can create your own config and pass it to Spread object constructor.
But yes, this part is really badly documented.
So, this code works well for me:
from gspread_pandas import Spread, conf
c = conf.get_config('[Your Path]', '[Your filename]')
spread = Spread('username', 'spreadname', config=c)
Thank you for this. It really should be documented better. I was getting so frustrated trying to get this to work with heroku, but it worked perfectly. I had to change to the following:
c = gspread_pandas.conf.get_config('/app/', 'google_secret.json')
spread = gspread_pandas.Spread('google_sheet_key_here_that_is_a_long_string', config=c)

Open BytesIO (xlsx) with xlrd

I'm working with Django and need to read the sheets and cells of an uploaded xlsx file. It should be possible with xlrd but because the file has to stay in memory and may not be saved to a location I'm not sure how to continue.
The start point in this case is a web page with an upload input and a submit button. When submitted the file is caught with request.FILES['xlsx_file'].file and send to a processing class that would have to extract all the important data for further processing.
The type of request.FILES['xlsx_file'].file is BytesIO and xlrd is not able to read that type because of no getitem methode.
After converting the BytesIO to StringIO the error messages seems to stay the same '_io.StringIO' object has no attribute '__getitem__'
file_enc = chardet.detect(xlsx_file.read(8))['encoding']
xlsx_file.seek(0)
sio = io.StringIO(xlsx_file.read().decode(encoding=file_enc, errors='replace'))
workbook = xlrd.open_workbook(file_contents=sio)
I'm moving my comment into an answer of it's own. It related to the example code (which includes decoding) given in the updated question:
Ok, thanks for your pointers. I downloaded xlrd and tested it locally. It seems the best way to go here is to pass it a string ie. open_workbook(file_contents=xlsx_file.read().decode(encoding=file_enc, errors='replace')). I misunderstood the docs, but I'm positive that file_contents= will work with a string.
Try xlrd.open_workbook(file_contents=request.FILES['xlsx_file'].read())
I had a similar problem but in my case I needed to unit test a Djano app with download by the user of an xls file.
The basic code using StringIO worked for me.
class myTest(TestCase):
def test_download(self):
response = self.client('...')
f = StringIO.StringIO(response.content)
book = xlrd.open_workbook(file_contents = f.getvalue() )
...
#unit-tests here

Categories