To share Python programs with users who don't have Python installed, you need to bundle your script into a standalone executable. PyInstaller is the industry standard tool for this task.
This guide covers basic conversion, GUI applications, and troubleshooting common issues.
Installation
pip install pyinstallerBasic Conversion
Create a single executable file:
pyinstaller --onefile myscript.pyThe executable appears in the dist/ folder:
project/
├── myscript.py
├── build/ # Temporary build files
├── dist/
│ └── myscript.exe # Your executable
└── myscript.spec # Build configurationGUI Applications (No Console)
For Tkinter, PyQt, or other GUI apps, hide the console window:
pyinstaller --onefile --noconsole app.pyOr use the shorthand:
pyinstaller -F -w app.pyAdding a Custom Icon
pyinstaller --onefile --icon=myicon.ico app.pyCommon Flags Reference
| Flag | Short | Purpose |
|---|---|---|
--onefile |
-F |
Bundle everything into single EXE |
--noconsole |
-w |
Hide console window (GUI apps) |
--console |
-c |
Show console (default) |
--icon=file.ico |
-i |
Set application icon |
--name=AppName |
-n |
Custom executable name |
--noconfirm |
-y |
Overwrite without asking |
--clean |
Clean cache before building | |
--add-data |
Include additional files |
Including Data Files
Bundle additional resources like images, configs, or databases:
pyinstaller --onefile --add-data "images;images" --add-data "config.json;." app.py
pyinstaller --onefile --add-data "images:images" --add-data "config.json:." app.pyAccessing Bundled Files in Code
import sys
import os
def resource_path(relative_path):
"""Get path to resource, works for dev and PyInstaller."""
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.dirname(__file__), relative_path)
config_file = resource_path("config.json")
image_path = resource_path("images/logo.png")Using a Spec File
For complex builds, customize the .spec file:
pyinstaller --onefile --noconsole --name MyApp app.py
pyinstaller myapp.specExample Spec File Modifications
a = Analysis(
['app.py'],
pathex=[],
binaries=[],
datas=[
('images', 'images'),
('config.json', '.'),
],
hiddenimports=['pandas', 'numpy'], # Add missing imports
...
)Hidden Imports
When PyInstaller misses dynamic imports:
pyinstaller --onefile --hidden-import=sklearn.neighbors app.pyOr in the spec file:
hiddenimports=['sklearn.neighbors', 'pandas._libs.tslibs.timedeltas']Reducing File Size
Exclude Unused Modules
pyinstaller --onefile --exclude-module matplotlib --exclude-module tkinter app.pyUse Virtual Environment
Build from a clean virtual environment with only required packages:
python -m venv build_env
build_env\Scripts\activate
pip install pyinstaller
pip install -r requirements.txt # Only what's needed
pyinstaller --onefile app.pyUse UPX Compression
pyinstaller --onefile --upx-dir=/path/to/upx app.pyCross-Platform Building
Troubleshooting
Antivirus False Positives
Windows Defender often flags unsigned PyInstaller EXEs:
Solutions:
- Digitally sign your executable
- Submit for analysis at Microsoft Security Intelligence
- Whitelist the
dist/folder
Missing DLLs or Modules
pyinstaller --onefile --debug=all app.pyApplication Won't Start
Add console to debug:
pyinstaller --onefile --console app.pyModule Not Found Errors
import sys
if getattr(sys, 'frozen', False):
import os
os.chdir(sys._MEIPASS)Alternative Tools
| Tool | Pros | Cons |
|---|---|---|
| PyInstaller | Most popular, well-documented | Large file size |
| cx_Freeze | Cross-platform | More configuration needed |
| Nuitka | Compiles to C, faster execution | Longer build time |
| py2exe | Windows-focused | Windows only |
Complete Build Script
import subprocess
import shutil
import os
def build():
for folder in ['build', 'dist']:
if os.path.exists(folder):
shutil.rmtree(folder)
cmd = [
'pyinstaller',
'--onefile',
'--noconsole',
'--name=MyApp',
'--icon=assets/icon.ico',
'--add-data=assets;assets',
'--clean',
'main.py'
]
subprocess.run(cmd, check=True)
print("Build complete! Check dist/MyApp.exe")
if __name__ == '__main__':
build()Summary
| Scenario | Command |
|---|---|
| Basic EXE | pyinstaller --onefile script.py |
| GUI app | pyinstaller -F -w app.py |
| With icon | pyinstaller -F -w -i icon.ico app.py |
| With data | pyinstaller -F --add-data "data;data" app.py |
| Debug build | pyinstaller -F -c --debug=all app.py |