Add or replace data files to existing PyInstaller exe (--onefile) - python

I use PyInstaller to package a one-file executable. The task I have been assigned is to create webservice that can return the PyInstaller executable as an attachment.
Depending on the user that is logged in I need to interchange a user-data file inside the executable. I know I can do this by rebuilding the executable on every request, but since the executables per user will be identical only differing by the data file mentioned above and since the build takes up both a time and processing power, I was wondering if it is possible to post-edit the PyInstaller executable and then either add or replace the user-data file contained in the .exe file.
In other words use a PyInstaller executable as template for the final user executables.
EDIT: This is what I have tried until now:
>>> from PyInstaller.loader import carchive
>>> arc = carchive.CArchive(path_to_exe)
>>> arc.add(('README.rst','C:\\temp\\pyinstaller-2.0\\README.rst',1,'s'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "PyInstaller\loader\carchive.py", line 245, in add
self.lib.write(s)
IOError: File not open for writing
Clearly this is goes nowhere cause opening an executable with carchive is appearantly always a read-only operation.
Best Regards
Jakob Simon-Gaarde

Related

Convert python file into exe using auto-py-to-exe

I have just finished a project where I have made a connect 4 game and am trying to convert it to an exe file using auto-py-to-exe.
I want to use the one-file option, however every time it finishes and I run it, it would come up with an error:
Failed to execute script 'main' due to unhandled exception: No file 'Assets/icon.png' found in working directory '...'
Then in the box it says:
Traceback (most recent call last):
File "main.py", line 32, in <module>
FileNotFoundError: No file 'Assets/window-icon.png' found in working directory '...'.
I've tried quite a few alterations, e.g. not using the image, but then it would come up with the same error but for a different added file.
How can I fix this?
EDIT: I've tried it again by using the os module and giving the full directories to all the files in main.py, but that hasn't changed anything.
I was also facing this problem then I got the solutions from analysing other threads.
You have to update your script by adding this function
import sys
import os
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
base_path=getattr(sys,'_MEIPASS',os.path.dirname(os.path.abspath(__file__)))
return os.path.join(base_path, relative_path)
and changing address of every additional files used in your script
for example
icon = resource_path("game.icon")
You just have to use auto-py-to-exe just like shown below
Showing the options you can just use
Add all the files used in the Additional File section of it and make sure that the period(.) is there in the destination
That's it !

Pyinstaller builds file but chokes on certain .dlls

When I run my py program it works the way I intended it to. If I am on a Linux box and build an executable using Pyinstaller, it builds without issue and executes without issue. I have scoured the Pyinstaller docs, git, etc. none of the posted fixes helped
I am still very new at python and feel like it might be a simple fix and might be over thinking the issue
Why can I no build a functional .exe on a windows based system using pyinstaller?
Windows 10 system
Pyinstaller version 3.2
Python version 3.5.2
This is a GUI program using appJar which is also up to date.
The file does build, but errors "Could not execute script"
EDIT
Not sure if this is best to edit in line like this but...
So studying the output and making adjustments, the issue seems to be appJar.py. For some reason it is missing assets, I am looking into it. The trouble is that I am still not used to looking at this kind of output and am not sure where to start.
C:\Users\_User_>C:\temp\fileCreatorGUI\fileCreatorGUI.exe
Traceback (most recent call last):
File "F:\Users\_User_\python_working\fileCreatorGUI.py", line 73, in <module>
app = gui()
File "C:\Users\_User_\AppData\Local\Programs\Python\Python35\lib\site-packages\appJar\appjar.py", line 509, in __init__
self.topLevel.wm_iconbitmap(self.appJarIcon)
File "C:\Users\_User_\AppData\Local\Programs\Python\Python35\lib\tkinter\__init__.py", line 1716, in wm_iconbitmap
return self.tk.call('wm', 'iconbitmap', self._w, bitmap)
_tkinter.TclError: bitmap "C:\temp\fileCreatorGUI\appJar\resources\icons\favicon.ico" not defined
Failed to execute script fileCreatorGUI
Edit 2
See answer below, but I was barking up the wrong tree on this one,
The Pyinstaller output chokes on the .dll's:
api-ms-win-core-console-l1-1-0.dll
api-ms-win-core-datetime-l1-1-0.dll
(There are like ~40 of these)
I added those .dll's to the python path, I declared them in the bianaries in the .spec file.
here is a truncated log:
2414 WARNING: Can not get binary dependencies for file: C:\Windows\system32\api-
ms-win-crt-stdio-l1-1-0.dll
Traceback (most recent call last):
File "C:\Users\_USER_NAME\AppData\Local\Programs\Python\Python35-32\lib\site-pa
ckages\PyInstaller\depend\bindepend.py", line 695, in getImports
return _getImports_pe(pth)
File "C:\Users\_USER_NAME\AppData\Local\Programs\Python\Python35-32\lib\site-pa
ckages\PyInstaller\depend\bindepend.py", line 122, in _getImports_pe
dll, _ = sym.forwarder.split('.')
TypeError: a bytes-like object is required, not 'str'
2423 WARNING: Can not get binary dependencies for file: C:\Windows\system32\api-
ms-win-crt-heap-l1-1-0.dll
I tried the fix listed here:
https://github.com/pyinstaller/pyinstaller/pull/1981
but it did not seem to make a difference.
Someone recommended adding the sys.path.insert() route but it did not make a difference either way
I also tried this in a VM with windows 7, clean install, no change. My next step is to try to use Wine in Debian, but I don't really want to go that route. Any help would be appreciated. Thank you
Turns out this was an appJar/packaging issue, the pyinstaller was not looking in the correct directory for the assets. per the dev of appJar, I commented out two lines of code in the appJar.py, lines 508-509:
if self.platform == self.WINDOWS:
self.topLevel.wm_iconbitmap(self.appJarIcon)
More on the specifics here: https://github.com/jarvisteach/appJar/issues/84
I probably can fix this by using the --path argument with pyinstaller but for the moment, the issue is fully resolved

python - compiled calling config file

I'm compiling a python application using pyinstaller.
The structure is like so -
d:\app\myprog.exe
d:\app\config\settings.conf
If I run myprog.exe --switch value from d:\app it runs fine, if I try to run from anywhere else like c:\windows it's not finding my settings.conf file complaining with the message:
Traceback (most recent call last):
File "<string>", line 284, in <module>
File "<string>", line 218, in main
File "ConfigParser.py", line 330, in get
ConfigParser.NoSectionError: No section: 'database'
myApp returned -1
database being the first line in the config file I'm trying to reference.
I'm referencing the BASE_DIR from the app here -
# Global Path and Config Info
try:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
except NameError: # We are the main py2exe script, not a module
BASE_DIR = os.path.dirname(os.path.abspath(sys.argv[0]))
So that I can get the config file like this -
config = RawConfigParser()
config.read(os.path.join(BASE_DIR, 'config/settings.conf'))
But I guess BASE_DIR is whatever folder I'm running the EXE from (like c:\windows, not the location of the EXE (which is d:\app)?
As mentioned in PyInstaller documentation (in How the One-File Program Works section) when a single executable file (created by PyInstaller) is executed, what happens is that a directory structure of the required library modules (Python VM, libraries and packages, etc.) are extracted from that single executable to a temporary directory, and then the application is started from there.
This means that __file__ in your Python code is not going to be the path to the single executable file, and that's the reason for this issue.
I'd write my app to accept the path to the configuration file from command line arguments, fall back to using an environment variable if available, and then a hard coded default value based on the platform (as most multi platform applications do to reach to their configurations).
Another approach is to use the one-directory output from PyInstaller and include the configuration file in the same directory.
Then to help make it easier to distribute the software to end users, use another tool to create a single Windows installer file from that directory structure. This way you'll be distributing the Windows installer, so users can run to install the application. But when the app runs, it runs from the directory where the configuration file resides.

py2exe PackageNotFoundError

I'm currently trying to package a Tkinter app into a .exe file using py2exe. The packaging works fine, and up until a point, the program functions. When I call a certain function, though, running the .exe file logs the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "Tkinter.pyc", line 1532, in __call__
File "/Users/Gordon/Gordon's Files/AutoFormatter/lib\formatterApp.py", line 58, in go
File "formatter.pyc", line 72, in take
File "docx\api.pyc", line 25, in Document
File "docx\opc\package.pyc", line 116, in open
File "docx\opc\pkgreader.pyc", line 32, in from_file
File "docx\opc\phys_pkg.pyc", line 31, in __new__
PackageNotFoundError: Package not found at 'C:\Users\Gordon\Gordon's Files\AutoFormatter\dist\library.zip\docx\templates\default.docx'
Upon originally running py2exe, I checked the \docx\ folder and found that py2exe hadn't actually copied over the \templates\ folder. After manually unzipping the library.zip, adding in the \templates\ folder in the right place, and then manually re-zipping, however, I get the same error.
My setup.py is as follows:
from distutils.core import setup
import py2exe
setup(
windows=[{'script': 'AutoFormatter.py'}],
options={
'py2exe':
{
'includes': ['lxml.etree', 'lxml._elementpath', 'gzip', 'docx'],
}
}
)
I'm running the program on a Windows 7 computer using Python 2.7.8 and py2exe 0.6.9.
This might be too late but I have been having the same troubles as well. I don't know if python-docx was made to be compiled into a single executable yet, none the less I have found solution.
I am on pyinstaller with python2.7, essentially the same thing. I hope that you are freezing into one directory rather than one file. This won't work if you're freezing to one file
Download this here(Mediafire link)
Place it in
C:\Users\Gordon\Gordon's Files\AutoFormatter\dist\library.zip\docx\templates\default.docx
basically wherever your .exe is in.
Hopefully that does the trick
Based off of me scouring through my own directories and the docx module, when you create a document:
doc = Document()
doc.save('hello.docx')
It pulls a template for you to use, if you do not create your own, it will use the default template offered by python-docx itself.
Don't quote me on this, but I believe python-docx looks through its own directories to find the default.docx template when executing it through python.
Since we compiled the script, the path changed to the directory in which the .exe is placed, however pyinstaller (or in your case py2exe) does not include the template with the dist folder, and this creates the PackageNotFoundError

Py2exe isn't copying webdriver_prefs.json into builds

I'm using py2exe to compile a Python 2.7 script that uses Selenium 2.39.0 to open up Firefox windows and carry out some routines. In the past, I've been able to compile the code without any issue. Today though, after updating from Selenium 2.35 to 2.39, I'm running into trouble. When I try to run the .exe generated by the compiled code, I get the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "Tkinter.pyo", line 1410, in __call__
File "literatureonlineapi2.5.5.py", line 321, in startapi
File "selenium\webdriver\firefox\webdriver.pyo", line 43, in __init__
File "selenium\webdriver\firefox\firefox_profile.pyo", line 58, in __init__
IOError: [Errno 2] No such file or directory: 'C:\\Text\\Professional\\Digital H
umanities\\Programming Languages\\Python\\Query Literature Online\\LION 1.0\\2.5
\\2.5.5\\dist\\.\\selenium\\webdriver\\firefox\\webdriver_prefs.json'
Here we go!
Exception in Tkinter callback
Traceback (most recent call last):
File "Tkinter.pyo", line 1410, in __call__
File "literatureonlineapi2.5.5.py", line 321, in startapi
File "selenium\webdriver\firefox\webdriver.pyo", line 43, in __init__
File "selenium\webdriver\firefox\firefox_profile.pyo", line 58, in __init__
IOError: [Errno 2] No such file or directory: 'C:\\Text\\Professional\\Digital H
umanities\\Programming Languages\\Python\\Query Literature Online\\LION 1.0\\2.5
\\2.5.5\\dist\\.\\selenium\\webdriver\\firefox\\webdriver_prefs.json'
(This error does not appear when I run the uncompiled code.)
I came across a google code page that led me to believe newer versions of Selenium have had trouble with this missing webdriver_prefs.json file, but that didn't help me sort out the problem.
Does anyone know how I might manually provide the missing file? I would be grateful for any help others can offer.
I found a solution, and thought I would post it in case others have a similar problem. I found the missing webdriver_prefs.json file tucked away in
C:\Python27\Lib\site-packages\selenium-2.39.0-py2.7.egg\selenium\webdriver\firefox\
After I had navigated to that directory, I grabbed the webdriver_prefs.json file and the webdriver.xpi file. I then copied both of those files into
dist\selenium\webdriver\firefox\
created by py2exe, and was able to run the compiled code as expected. God save the queen.
I did the following to fix the problem:
Create a sub-folder \selenium\webdriver\firefox\ under dist.
Under command DOS prompt, enter python.exe setup_firefox.py
You could either running the executable under dist or copy all the files under "dist" to your own directory and run the executable from there.
Here is my setup_firefox.py:
from distutils.core import setup
import py2exe,sys,os
sys.argv.append('py2exe')
setup(
console=[{'script':"test.py"}],
options={
"py2exe":{
"skip_archive": True,
"unbuffered": True,
"optimize": 2
},
}
)
I had a related issue for which I have found a work round...
My issue
I was trying to run a python script that uses Selenium 2.48.0 and worked fine on the development machine but failed to open Firefox when py2exe'ed with the error message:
[Errno 2] No such file or directory:'C:\test\dist\library.zip\selenium\webdriver\firefox\webdriver_prefs.json'
Cause
I traced the problem to the following file in the selenium package
C:\Python27\Lib\site-packages\selenium\webdriver\firefox\firefox_profile.py
It was trying to open webdriver_prefs.json and webdriver.xpifrom the same parent directory
This works fine when running on the development machine but when the script is run through py2exe firefox_profile.pyc is added to library.zip but webdriver_prefs.json and webdriver.xpi aren't.
Even if you manual add these files to appropriate location in the zip file you will still get the 'file not found' message.
I think this is because the Selenium file can't cope with opening files from within the zip file.
Work Round
My work round was to get py2exe to copy the two missing files to the dist directory and then modify firefox_profile.py to check the directory string.
If it contained .zip modify the string to look in the parent directory
webdriver_prefs.json
class FirefoxProfile(object):
def __init__(self, profile_directory=None):
if not FirefoxProfile.DEFAULT_PREFERENCES:
'''
The next couple of lines attempt to WEBDRIVER_PREFERENCES json file from the directory
that this file is located.
However if the calling script has been converted to an exe using py2exe this file will
now live within a zip file which will cause the open line to fail with a 'file not found'
message. I think this is because open can't cope with opening a file from within a zip file.
As a work round in our application py2exe will copy the preference to the parent directory
of the zip file and attempt to load it from there
'''
if '.zip' in os.path.join(os.path.dirname(__file__)) :
# find the parent dir that contains the zipfile
parentDir = __file__.split('.zip')[0]
configFile = os.path.join(os.path.dirname(parentDir), WEBDRIVER_PREFERENCES)
print "Running from within a zip file, using [%s]" % configFile
else:
configFile = os.path.join(os.path.dirname(__file__), WEBDRIVER_PREFERENCES)
with open(configFile) as default_prefs:
FirefoxProfile.DEFAULT_PREFERENCES = json.load(default_prefs)
webdriver.xpi
def _install_extension(self, addon, unpack=True):
if addon == WEBDRIVER_EXT:
addon = os.path.join(os.path.dirname(__file__), WEBDRIVER_EXT)
tmpdir = None
xpifile = None
'''
The next couple of lines attempt to install the webdriver xpi from the directory
that this file is located.
However if the calling script has been converted to an exe using py2exe this file will
now live within a zip file which will cause the script to fail with a 'file not found'
message. I think this is because it can't cope with opening a file from within a zip file.
As a work round in our application py2exe will copy the .xpi to the parent directory
of the zip file and attempt to load it from there
'''
if '.zip' in addon :
# find the parent dir that contains the zipfile
parentDir = os.path.dirname(addon.split('.zip')[0])
addon = os.path.join(parentDir, os.path.basename(addon))
print "Running from within a zip file, using [%s]" % addon
if addon.endswith('.xpi'):
tmpdir = tempfile.mkdtemp(suffix='.' + os.path.split(addon)[-1])
compressed_file = zipfile.ZipFile(addon, 'r')
for name in compressed_file.namelist():
if name.endswith('/'):
if not os.path.isdir(os.path.join(tmpdir, name)):
os.makedirs(os.path.join(tmpdir, name))
else:
if not os.path.isdir(os.path.dirname(os.path.join(tmpdir, name))):
os.makedirs(os.path.dirname(os.path.join(tmpdir, name)))
data = compressed_file.read(name)
with open(os.path.join(tmpdir, name), 'wb') as f:
f.write(data)
xpifile = addon
addon = tmpdir
I found the sulution, the py2exe can't open zip file. So after copy the webdriver_prefs.json and webdriver.xpi, decompression the library.zip into a folder named "library.zip"

Categories