How to compile .exe with Pyinstaller using --onefile and --add-data options
The solution is to modify your code so that it uses run-time information to know if it is running from the bundled .exe
or as the original Python source, and have it use a different file-system path to the data files accordingly.
You are currently trying to load your data files from the current working directory, which is usually the directory that the .exe
resides in. However, the data files are in the MEI****
folder that you mention.
For reference, this is the relevant passage from the PyInstaller documentation:
When your program is not frozen, the standard Python variable
__file__
is the full path to the script now executing. When a bundled app starts up, the bootloader sets thesys.frozen
attribute and stores the absolute path to the bundle folder insys._MEIPASS
. For a one-folder bundle, this is the path to that folder, wherever the user may have put it. For a one-file bundle, this is the path to the_MEIxxxxxx
temporary folder created by the bootloader.
As a simple example, say you have a Python script app.py
that simply prints out the content of a data file named data.txt
, stored alongside the script. You would bundle it by running pyinstaller --onefile --add-data="data.txt;." app.py
on the command line. To make sure the Python script app.py
and the bundled executable app.exe
both find the data, the source code could be this:
import sys
from pathlib import Path
if getattr(sys, 'frozen', False):
folder = Path(sys._MEIPASS)
else:
folder = Path(__file__).parent
file = folder/'data.txt'
data = file.read_text()
print(data)
Include multiple data files with pyinstaller
The answer was in https://media.readthedocs.org/pdf/pyinstaller/cross-compiling/pyinstaller.pdf which indicates I can simply use the --add-data option multiple times!
Adding textfile with --onefile option
When you add your text file with one-dir
, Pyinstaller would put your file next to your executable and you will be fine when accessing your file by the script (e.g open("myfile.txt")
. But when you create your executable with --onefile
it would extract your file in a temp directory which is a separate directory, so calling open("myfile.txt")
would result in a NotFound
error because the file doesn't exist besides your executable. So you need to change the path to point to the temp directory. The sys._MEIPASS
would return the temp directory so you need to locate your files inside it. You can find more info in here.
A function like this would solve the problem:
import sys
import os
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
Then you can access your file with source = resource_path("myfile.txt")
.
Related Topics
Get Only Unique Words from a Sentence in Python
How to Read Numbers from File in Python
Json.Decoder.Jsondecodeerror: Expecting Value: Line 1 Column 1 (Char 0) Python
Pandas Filtering for Multiple Substrings in Series
How to Extract Hours and Minutes from a Datetime.Datetime Object
Print() Prints Only Every Second Input
Convert Regular Python String to Raw String
Python Pandas .Isnull() Does Not Work on Nat in Object Dtype
Return Value from Python-Shell as Response
How to Get the Sum of a CSV Column List to Print
Python Ttk Treeview: How to Select and Set Focus on a Row
How to Plot Multiple Pandas Columns
Finding the Most Frequent Character in a String
Regex to Remove Commas Before a Number in Python
How to Clear Only Last One Line in Python Output Console
How to Store Python Dictionary in to MySQL Db Through Python