cx_freeze and docx - problems when freezing - python

I have a simple program that takes input from the user and then does scraping with selenium. Since the user doesn't have Python environment installed I would like to convert it to *.exe. I usually use cx_freeze for that and I have successfully converted .py programs to .exe. At first it was missing some modules (like lxml) but I was able to solve it. Now I think I only have problem with docx package.
This is how I initiate the new document in my program (I guess this is what causes me problems):
doc = Document()
#then I do some stuff to it and add paragraph and in the end...
doc.save('results.docx')
When I run it from python everything works fine but when I convert to exe I get this error:
Traceback (most recent call last):
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\cx_Freeze\initscripts\Console.py", line 27, in <module>
exec(code, m.__dict__)
File "tribunalRio.py", line 30, in <module>
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\docx\api.py", line 25, in Document
document_part = Package.open(docx).main_document_part
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\docx\opc\package.py", line 116, in open
pkg_reader = PackageReader.from_file(pkg_file)
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\docx\opc\pkgreader.py", line 32, in from_file
phys_reader = PhysPkgReader(pkg_file)
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\docx\opc\phys_pkg.py", line 31, in __new__
"Package not found at '%s'" % pkg_file
docx.opc.exceptions.PackageNotFoundError: Package not found at 'C:\Users\tyszkap\Dropbox (Dow Jones)\Python Projects\build\exe.win-a
md64-3.4\library.zip\docx\templates\default.docx'
This is my setup.py program:
from cx_Freeze import setup, Executable
executable = Executable( script = "tribunalRio.py" )
# Add certificate to the build
options = {
"build_exe": {'include_files' : ['default.docx'],
'packages' : ["lxml._elementpath", "inspect", "docx", "selenium"]
}
}
setup(
version = "0",
requires = [],
options = options,
executables = [executable])
I thought that explicitly adding default.docx to the package would solve the problem (I have even tried adding it to the library.zip but it gives me even more errors) but it didn't. I have seen this post but I don't know what they mean by:
copying the docx document.py module inside my function (instead of
using Document()
Any ideas? I know that freezing is not the best solution but I really don't want to build a web interface for such a simple program...
EDIT:
I have just tried this solution :
def find_data_file(filename):
if getattr(sys, 'frozen', False):
# The application is frozen
datadir = os.path.dirname(sys.executable)
else:
# The application is not frozen
# Change this bit to match where you store your data files:
datadir = os.path.dirname(__file__)
return os.path.join(datadir, filename)
doc = Document(find_data_file('default.docx'))
but again receive Traceback error (but the file is in this location...):
Traceback (most recent call last):
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\cx_Freeze\initscripts\Console.py", line 27, in <module>
exec(code, m.__dict__)
File "tribunalRio.py", line 43, in <module>
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\docx\api.py", line 25, in Document
document_part = Package.open(docx).main_document_part
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\docx\opc\package.py", line 116, in open
pkg_reader = PackageReader.from_file(pkg_file)
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\docx\opc\pkgreader.py", line 32, in from_file
phys_reader = PhysPkgReader(pkg_file)
File "C:\Users\tyszkap\AppData\Local\Continuum\Anaconda3\lib\site-packages\docx\opc\phys_pkg.py", line 31, in __new__
"Package not found at '%s'" % pkg_file
docx.opc.exceptions.PackageNotFoundError: Package not found at 'C:\Users\tyszkap\Dropbox (Dow Jones)\Python Projects\build\exe.win-a
md64-3.4\default.docx'
What am I doing wrong?

I expect you'll find the problem has to do with your freezing operation not placing the default Document() template in the expected location. It's stored as package data in the python-docx package as docx/templates/default.docx (see setup.py here: https://github.com/python-openxml/python-docx/blob/master/setup.py#L37)
I don't know how to fix that in your case, but that's where the problem is it looks like.

I had the same problem and managed to get around it by doing the following. First, I located the default.docx file in the site-packages. Then, I copied it in the same directory as my .py file. I also start the .docx file with Document() which has a docx=... flag, to which I assigned the value: os.path.join(os.getcwd(), 'default.docx') and now it looks like doc = Document(docx=os.path.join(os.getcwd(), 'default.docx')). The final step was to include the file in the freezing process. Et voilĂ ! So far I have no problem.

Related

Unable to write to destination pdfkit python

Summary
I am trying to use the package pdfkit (version 0.6.1) and wkhtmltopdf (version 0.12.6.0) to convert HTML to PDF.
I can use the script, until I try to convert it to an .exe file using cx_freeze. Then it fails. Using the executable, I get the following error:
Error
Exception in thread Thread-109:
Traceback (most recent call last):
File "C:\Users\Diego\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 932, in _bootstrap_inner
self.run()
File "C:\Users\Diego\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\Diego\Pictures\Zona Python\GuideMath\src\interfaz\VentanaExportar.py", line 132, in __exportarHTMLaPDF
self.__resultado = self.__ctrl.exportarHTMLaPDF(
File "C:\Users\Diego\Pictures\Zona Python\GuideMath\src\controlador\Controlador.py", line 142, in exportarHTMLaPDF
return "E" if pdfkit.from_url(
File "C:\Users\Diego\AppData\Local\Programs\Python\Python38-32\lib\site-packages\pdfkit\api.py", line 26, in from_url
return r.to_pdf(output_path)
File "C:\Users\Diego\AppData\Local\Programs\Python\Python38-32\lib\site-packages\pdfkit\pdfkit.py", line 156, in to_pdf
raise IOError('wkhtmltopdf reported an error:\n' + stderr)
OSError: wkhtmltopdf reported an error:
Loading pages (1/6)
Warning: Javascript alert: Hola
QPainter::begin(): Returned false============================] 100%
Error: Unable to write to destination
Exit with code 1, due to unknown error.
Relevant Code Snippet
pdfConf = pdfkit.configuration(wkhtmltopdf=os.path.join(
self.__principalPath, "docs/web/PDF/wkhtmltopdf.exe"))
pdfOpti = {
"window-status": "print",
"dpi": "90",
# "quiet": "",
"page-size": "A4",
"footer-html": f"{url}/footer.html"
}
try:
return "E" if pdfkit.from_url(
url=f"{url}/document.html",
output_path=outputPath,
configuration=pdfConf,
options=pdfOpti) else "F"
except:
return "F"
Other
I searched on the internet and I found that the problem might be the destination path. But I am using the complete path: C:\Users\Diego\Desktop\JAA.pdf
Setup File
import sys
from cx_Freeze import setup, Executable
build_exe_options = {"packages": ["ctypes", "os", "wx", "json", "threading", "pdfkit"],
"excludes": ["tkinter"]}
base = None
if sys.platform == "win32":
base = "Win32GUI"
setup(name="App",
version="0.1",
description="",
options={"build_exe": build_exe_options},
executables=[Executable("main.py", base=base, icon="app.ico")])
The diagnosis you made is correct (after all, Error: Unable to write to destination is pretty straightforward).
If you can create the output file, but the created EXE file cannot - and we know it couldn't - then this means that either
the real destination path is not actually there when the EXE runs. You think you're writing to a given path, but somehow the EXE attempts to write elsewhere.
the EXE has no permissions to create the file.
This might be caused by several reasons. The EXE file, for security reasons, might output in a given directory, so when you ask for "C:\somefile", it actually tries to write in "%TEMP%/C:/somefile". Or might refuse to use a full path.
Or you could need to write the file in Unix syntax ("C:/Users/..." instead of "C:\Users...", because e.g. "\Diego" is translated to "0x0Diego" ("iego" with a carriage return prefixed).
You should therefore insert into the EXE file some code to explore the filesystem the EXE is seeing (which might not be the same one you see!), and/or try to write to a different path, perhaps indeed %TEMP% (where permissions should be sufficient).

Owlrady2 Load imported onotologies from file not from url

I'm trying to load an ontology that imports some other ontologies. These other ontologies are located on my machine and not on the web. If I try my_ontology.load() it tries to load these other ontologies from their URI. But that fails.
How can I tell owlready2 where these ontologies are located?
The traceback looks like this:
Traceback (most recent call last):
File "~Software/PyOntologyChecker/app.py", line 12, in <module>
covid_onto = get_ontology_from_file(COVID_ONTOLOGY)
File "~Software/PyOntologyChecker/app.py", line 8, in get_ontology_from_file
return get_ontology("file://" + file_path).load()
File "~.local/lib/python3.8/site-packages/owlready2/namespace.py", line 813, in load
imported_ontologies = [self.world.get_ontology(self._unabbreviate(abbrev_iri)).load() for abbrev_iri in self.world._get_obj_triples_sp_o(self.storid, owl_imports)]
File "~.local/lib/python3.8/site-packages/owlready2/namespace.py", line 813, in <listcomp>
imported_ontologies = [self.world.get_ontology(self._unabbreviate(abbrev_iri)).load() for abbrev_iri in self.world._get_obj_triples_sp_o(self.storid, owl_imports)]
File "~.local/lib/python3.8/site-packages/owlready2/namespace.py", line 773, in load
except: raise OwlReadyOntologyParsingError("Cannot download '%s'!" % f)
owlready2.base.OwlReadyOntologyParsingError: Cannot download 'http://ontofox.hegroup.org/CHEBI_module11.owl'!
Assuming you have a local copy of CHEBI_module11.owl dependence (ex. SOME_DIR/HEBI_module11.owl), add its location into onto_path (as stated in the docs):
onto_path.append("SOME_DIR")
before doing my_ontology.load(). That tells Owlready2 to look for files in SOME_DIR. The path can be relative or absolute (like c:/data/ontologies).
The current Python's working directory is not scanned by default but can be set so with onto_path.append(".").
If a required ontology is described by URI not ending with a usual filename, like "http://rdf.webofcode.org/woc/", the local filename (woc in this case) should not have an extension.

I am having issues with using pynsist

Hi I've installed pynsist in order to make my python files into executables but I am having some issues. The project consists of two files that I've written. The main program to be run is Filereader.py and a supplied file called spuriousReq.py which Filereader.py uses a function from. Currently my installer.cfg file looks like this
[Application]
name=WFilereader
version=1.0
entry_point=Filereader
console=true
[Python]
version=3.4.0
[Include]
packages = matplotlib
statistics
bisect
files = spuriousReq.py
I've moved the installer.cfg file and both python files to the C:\Python34\Scripts folder in order to access them from the cmd (Yes I am new at this..). But I get the following error which I dont know how to interpret or solve..
C:\Python34\Scripts>"C:\Python34\python.exe" "C:\Python34\Scripts\\pynsist" inst
aller.cfg
Traceback (most recent call last):
File "C:\Python34\Scripts\\pynsist", line 3, in <module>
main()
File "C:\Python34\lib\site-packages\nsist\__init__.py", line 393, in main
shortcuts = configreader.read_shortcuts_config(cfg)
File "C:\Python34\lib\site-packages\nsist\configreader.py", line 172, in read_
shortcuts_config
appcfg = cfg['Application']
File "C:\Python34\lib\configparser.py", line 937, in __getitem__
raise KeyError(key)
KeyError: 'Application'
As mentioned in the documentation [http://pynsist.readthedocs.io/en/latest/] you need to specify the function from your 'Filereader.py' file which will be starting the execution of your script. For example if you have a 'main' function that will be the entry point or starting point of your script then you need to specify that in your 'installer.cfg' file like below:-
[Application]
name=WFilereader
version=1.0
entry_point=Filereader:main <------ Here mention your entry point function.
console=true
[Python]
version=3.4.0
[Include]
packages = matplotlib
statistics
bisect
files = spuriousReq.py

How can I appropriately include external resources with cxfreeze?

I'm trying to use cxfreeze to build my Python scripts into an .exe file. However my scripts use some external data files which aren't being packaged into the libary.zip file created.
For example, my scripts are located in src/, and the external data is located in src/data/. I've specified the include_files property in the build_exe_options, but this only copies the directory and files into the built directory; it doesn't add them to library.zip, which is where the scripts end up looking for the files.
Even if I go in to the created library.zip and manually add the data directory, I receive the same error. Any idea how to get cxfreeze to package these external resources appropriately?
setup.py
from cx_Freeze import setup, Executable
build_exe_options = {"includes" : ["re"], "include_files" : ["data/table_1.txt", "data/table_2.txt"]}
setup(name = "My Script",
version = "0.8",
description = "My Script",
options = { "build_exe" : build_exe_options },
executables = [Executable("my_script.py")])
fileutil.py (where it tries to read the resource files)
def read_file(filename):
path, fl = os.path.split(os.path.realpath(__file__))
filename = os.path.join(path, filename)
with open(filename, "r") as file:
lines = [line.strip() for line in file]
return [line for line in lines if len(line) == 0 or line[0] != "#"]
... called with ...
read_file("data/table_1.txt")
Error Traceback
Traceback (most recent call last):
File "C:\Python33\lib\site-packages\cx_Freeze\initscripts\Console3.py", line 2
7, in <module> exec(code, m.__dict__)
File "my_script.py", line 94, in <module>
File "my_script.py", line 68, in run
File "C:\workspaces\py\test_script\src\tables.py", line 12, in load_data
raw_gems = read_file("data/table_1.txt")
File "C:\workspaces\py\test_script\src\fileutil.py", line 8, in read_file
with open(filename, "r") as file:
FileNotFoundError: [Errno 2] No such file or directory:
'C:\\workspaces\\py\\test_script\\src\\build\\exe.win32-3.3\\library.zip\\data/table_1.txt'
The following structure worked for me:
|-main.py
|-src
|-utils.py (containing get_base_dir())
|-data
then refer to your data always relative to the location of main.py that you receive through the following function within the src directory:
import os, sys, inspect
def get_base_dir():
if getattr(sys,"frozen",False):
# If this is running in the context of a frozen (executable) file,
# we return the path of the main application executable
return os.path.dirname(os.path.abspath(sys.executable))
else:
# If we are running in script or debug mode, we need
# to inspect the currently executing frame. This enable us to always
# derive the directory of main.py no matter from where this function
# is being called
thisdir = os.path.dirname(inspect.getfile(inspect.currentframe()))
return os.path.abspath(os.path.join(thisdir, os.pardir))
If you include the data according to the cx_Freeze documentation, it will be in the same directory as the .exefile (i.e. not in the zipfile), which will work with this solution.

py2exe/pyinstaller and DispatchWithEvents

I have a program that uses the win32com library to control iTunes, but have been having some issues getting it to compile into an executable. The problem seems to revolve around using DispatchWithEvents instead of Dispatch. I've created a very simple program to illustrate my problem:
import win32com.client
win32com.client.gencache.is_readonly = False #From py2exe wiki
class ITunesEvents(object):
def __init__(self): self.comEnabled = True
def OnCOMCallsDisabledEvent(self, reason): self.comEnabled = False
def OnCOMCallsEnabledEvent(self): self.comEnabled = True
# The first line works in the exe, the second doesn't.
itunes = win32com.client.Dispatch("iTunes.Application")
#itunes = win32com.client.DispatchWithEvents("iTunes.Application", ITunesEvents)
lib = getattr(itunes, "LibraryPlaylist")
src = getattr(lib, "Source")
playlists = getattr(src, "Playlists")
print "Found %i playlists." % getattr(playlists, "Count")
Using Dispatch, the program compiles and runs correctly. Using DispatchWithEvents, the program runs fine when called from the command line, but produces the following error when running the exe:
Traceback (most recent call last):
File "sandbox.py", line 16, in <module>
itunes = win32com.client.DispatchWithEvents("iTunes.Application", ITunesEvents)
File "win32com\client\__init__.pyc", line 252, in DispatchWithEvents
File "win32com\client\gencache.pyc", line 520, in EnsureModule
File "win32com\client\gencache.pyc", line 287, in MakeModuleForTypelib
File "win32com\client\makepy.pyc", line 259, in GenerateFromTypeLibSpec
File "win32com\client\gencache.pyc", line 141, in GetGeneratePath
IOError: [Errno 2] No such file or directory: '[distDir]\\library.zip\\win32com\\gen_py\\__init__.py'
I've also tried using PyInstaller, which gives a similar error:
File "<string>", line 16, in <module>
File "[outDir]/win32com.client", line 252, in DispatchWithEvents
File "[outDir]/win32com.client.gencache", line 520, in EnsureModule
File "[outDir]/win32com.client.gencache", line 287, in MakeModuleForTypelib
File "[outDir]/win32com.client.makepy", line 286, in GenerateFromTypeLibSpec
File "[outDir]/win32com.client.gencache", line 550, in AddModuleToCache
File "[outDir]/win32com.client.gencache", line 629, in _GetModule
File "[pyinstallerDir]\iu.py", line 455, in importHook
raise ImportError, "No module named %s" % fqname
ImportError: No module named win32com.gen_py.9E93C96F-CF0D-43F6-8BA8-B807A3370712x0x1x13
I know I can manually add the typelib in my setup.py file, but I'd like to run the code on computers with different versions of iTunes without recompiling so I'd prefer to dynamically create it. If there's no way to do this with the setup/spec, maybe there is another way to load the events? Thanks.
Addition:
Thanks to Ryan, I found I could take the generated py file and after a little digging, was able to come up with the following.
Take the generated py file (from makepy.py) and rename it somewhere like cominterface.py. Then you'll need to do the following to actually create the COM object with event handler.
import cominterface
from types import ClassType
from win32com.client import EventsProxy, _event_setattr_
class ItunesEvents:
'''iTunes events class. See cominterface for details.'''
def OnPlayerPlayEvent(self, t):print "Playing..."
def OnPlayerStopEvent(self, t): print "Stopping..."
itunes = cominterface.iTunesApp()
rClass = ClassType("COMEventClass", (itunes.__class__, itunes.default_source, ItunesEvents), {'__setattr__': _event_setattr_})
instance = rClass(itunes._oleobj_)
itunes.default_source.__init__(instance, instance)
#ItunesEvents.__init__(instance) #Uncomment this line if your events class has __init__.
itunes = EventsProxy(instance)
Then you can go about your business.
I was experiencing the exact same error.
This link put me in the right direction -->
http://www.py2exe.org/index.cgi/UsingEnsureDispatch
however it mentions that :
NB You must ensure that the python...\win32com.client.gen_py dir does not exist
to allow creation of the cache in %temp%
Which was a bit confusing.
What solved it for me was renaming "C:\Python26\Lib\site-packages\win32com\gen_py" to "C:\Python26\Lib\site-packages\win32com\gen_pybak" (when running py2exe)
This is the official way to do it.
(Edit: copied verbatim example from that link above)
import win32com.client
if win32com.client.gencache.is_readonly == True:
#allow gencache to create the cached wrapper objects
win32com.client.gencache.is_readonly = False
# under p2exe the call in gencache to __init__() does not happen
# so we use Rebuild() to force the creation of the gen_py folder
win32com.client.gencache.Rebuild()
# NB You must ensure that the python...\win32com.client.gen_py dir does not exist
# to allow creation of the cache in %temp%
# Use SAPI speech through IDispatch
from win32com.client.gencache import EnsureDispatch
from win32com.client import constants
voice = EnsureDispatch("Sapi.SpVoice", bForDemand=0)
voice.Speak( "Hello World.", constants.SVSFlagsAsync )
Instead of depending on the cache, I'd recommend going into the local cache directory, copying the generated file into your local project file, and naming it something like ITunesInterface.py, and calling to that explicitly. This will make py2exe pull it into your compiled app.

Categories

Resources