How to compile multiple python files into single .exe file using pyinstaller
The best way is to use the array datas
For example like this:
a = Analysis(['.\\main.py'],
pathex=['.'],
binaries=None,
datas=[ ('.\\Ressources\\i18n', 'i18n'),
('.\\Ressources\\file1.py', '.')],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
Note: Make sure to put it in the right relative path so your program will be able to access it
Edit: Given your error message, the problem is not in packaging with PyInstaller but in os.system command.
os.system is equivalent to opening a DOS command window and typping your command python_file.py
To access your python files, you have to know:
- PyInstaller unpack your packed files in a temporary folder that you can access in sys._MEIPASS (only works from the .exe)
os.system can be used to launch python given the complete path to the file like this:
os.system("python " + os.path.join(sys._MEIPASS, "python_file.py"))
But be carefull, this will only work if python is installed on the system (and included in syspath) and from the exe. Executing directly your python file will send exception.
How to compile multiple subprocess python files into single .exe file using pyinstaller
Assuming you can't restructure your app so this isn't necessary (e.g., by using multiprocessing
instead of subprocess
), there are three solutions:
- Ensure that the .exe contains the scripts as an (executable) zipfile—or just use
pkg_resources
—and copy the script out to a temporary directory so you can run it from there. - Write a multi-entrypoint wrapper script that can be run as your main program, and also run as each script—because, while you can't run a script out of the packed exe, you can import a module out of it.
- Using
pkg_resources
again, write a wrapper that runs the script by loading it as a string and running it withexec
instead.
The second one is probably the cleanest, but it is a bit of work. And, while we could rely on setuptools
entrypoints to some of the work, trying to explain how to do this is much harder than explaining how to do it manually,1 so I'm going to do the latter.
Let's say your code looked like this:
# main.py
import subprocess
import sys
spam, eggs = sys.argv[1], sys.argv[2]
subprocess.run([sys.executable, 'vikings.py', spam])
subprocess.run([sys.executable, 'waitress.py', spam, eggs])
# vikings.py
import sys
print(' '.join(['spam'] * int(sys.argv[1])))
# waitress.py
import sys
import time
spam, eggs = int(sys.argv[1]), int(sys.argv[2]))
if eggs > spam:
print("You can't have more eggs than spam!")
sys.exit(2)
print("Frying...")
time.sleep(2)
raise Exception("This sketch is getting too silly!")
So, you run it like this:
$ python3 main.py 3 4
spam spam spam
You can't have more eggs than spam!
We want to reorganize it so there's a script that looks at the command-line arguments to decide what to import. Here's the smallest change to do that:
# main.py
import subprocess
import sys
if sys.argv[1][:2] == '--':
script = sys.argv[1][2:]
if script == 'vikings':
import vikings
vikings.run(*sys.argv[2:])
elif script == 'waitress':
import waitress
waitress.run(*sys.argv[2:])
else:
raise Exception(f'Unknown script {script}')
else:
spam, eggs = sys.argv[1], sys.argv[2]
subprocess.run([sys.executable, __file__, '--vikings', spam])
subprocess.run([sys.executable, __file__, '--waitress', spam, eggs])
# vikings.py
def run(spam):
print(' '.join(['spam'] * int(spam)))
# waitress.py
import sys
import time
def run(spam, eggs):
spam, eggs = int(spam), int(eggs)
if eggs > spam:
print("You can't have more eggs than spam!")
sys.exit(2)
print("Frying...")
time.sleep(2)
raise Exception("This sketch is getting too silly!")
And now:
$ python3 main.py 3 4
spam spam spam
You can't have more eggs than spam!
A few changes you might want to consider in real life:
- DRY: We have the same three lines of code copied and pasted for each script, and we have to type each script name three times. You can just use something like
__import__(sys.argv[1][2:]).run(sys.argv[2:])
with appropriate error handling. - Use
argparse
instead of this hacky special casing for the first argument. If you're already sending non-trivial arguments to the scripts, you're probably already usingargparse
or an alternative anyway. - Add an
if __name__ == '__main__':
block to each script that just callsrun(sys.argv[1:])
, so that during development you can still run the scripts directly to test them.
I didn't do any of these because they'd obscure the idea for this trivial example.
1 The documentation is great as a refresher if you've already done it, but as a tutorial and explanatory rationale, not so much. And trying to write the tutorial that the brilliant PyPA guys haven't been able to come up with for years… that's probably beyond the scope of an SO answer.
Convert multiple .py file to .exe
The concept used to execute (dummy_script.py) in (GUI.py) is,
p = QProcess()
p.start("python3", ['dummy_script.py'])
PyInstaller is pretty good at finding and bundling dependencies, but it won't be able to figure out that GUI.py
needs dummy_script.py
if you run it this way.
A better approach would be to import the code you need and use it directly, e.g something like
from dummy_script import some_function
some_function()
Other optionsIf you simply modify GUI.py
like this, PyInstaller should find dummy_script.py
on its own.
If that is not practical for some reason, you should be able to declare it as a hidden import using a spec file. You may have a spec file from an earlier build, but if you need to create a new one you can do that with something like this:
pyi-makespec GUI.py
Then edit the spec file to add dummy_script
to the list of hidden imports:
a = Analysis(['GUI.py'],
# ...
hiddenimports=['dummy_script'],
# ...,
)
Then build again from the modified spec file:
pyinstaller foo.spec
That may not work either, since you still aren't import
ing the other module. In that case you may need to declare it as a data file instead.
Related Topics
Using a Global Variable With a Thread
How to Read Pdf Files One by One from a Folder in Python
Setting Matplotlib Colorbar Range
How to Repeat Each Test Multiple Times in a Py.Test Run
Unable to Install Psycopg2 (Pip Install Psycopg2)
How to Change the File Name of an Uploaded File in Django
Animate a Rotating 3D Graph in Matplotlib
Use Tqdm Progress Bar With Pandas
Splitting Dataframe into Multiple Dataframes
How to Test If a List Contains Another List as a Contiguous Subsequence
How to Enable Autocomplete (Intellisense) for Python Package Modules
Python Comparing Previous and Next Row Value
Error Opening File in H5Py (File Signature Not Found)
How to Deal With Certificates Using Selenium
Using Regex to Find All Phrases That Are Completely Capitalized
Open a Putty Window and Run Ssh Commands - Python
Best Practices for Adding .Gitignore File for Python Projects