How to Convert Python Script to EXE (Executable)
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 pyinstaller
Basic Conversion
Create a single executable file:
pyinstaller --onefile myscript.py
The executable appears in the dist/ folder:
project/
├── myscript.py
├── build/ # Temporary build files
├── dist/
│ └── myscript.exe # Your executable
└── myscript.spec # Build configuration
GUI Applications (No Console)
For Tkinter, PyQt, or other GUI apps, hide the console window:
pyinstaller --onefile --noconsole app.py
Or use the shorthand:
pyinstaller -F -w app.py
Adding a Custom Icon
pyinstaller --onefile --icon=myicon.ico app.py
Windows requires .ico format. Convert PNG to ICO using online tools or:
from PIL import Image
img = Image.open("icon.png")
img.save("icon.ico", format="ICO")
Common 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:
# Windows syntax (use ; separator)
pyinstaller --onefile --add-data "images;images" --add-data "config.json;." app.py
# Linux/macOS syntax (use : separator)
pyinstaller --onefile --add-data "images:images" --add-data "config.json:." app.py
Accessing 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'):
# Running as compiled
return os.path.join(sys._MEIPASS, relative_path)
# Running as script
return os.path.join(os.path.dirname(__file__), relative_path)
# Usage
config_file = resource_path("config.json")
image_path = resource_path("images/logo.png")
Using a Spec File
For complex builds, customize the .spec file:
# Generate spec file without building
pyinstaller --onefile --noconsole --name MyApp app.py
# Edit myapp.spec, then build from it
pyinstaller myapp.spec
Example Spec File Modifications
# myapp.spec
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.py
Or 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.py
Use 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.py
Use UPX Compression
# Download UPX and add to PATH, then:
pyinstaller --onefile --upx-dir=/path/to/upx app.py
Cross-Platform Building
PyInstaller can only create executables for the OS it runs on:
- Windows → .exe
- macOS → .app
- Linux → ELF binary
Use CI/CD services or VMs for cross-platform builds.
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
# Verbose mode shows what's included
pyinstaller --onefile --debug=all app.py
Application Won't Start
Add console to debug:
# Temporarily enable console to see errors
pyinstaller --onefile --console app.py
Module Not Found Errors
# Add at the top of your script before other imports
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
# build.py
import subprocess
import shutil
import os
def build():
# Clean previous builds
for folder in ['build', 'dist']:
if os.path.exists(folder):
shutil.rmtree(folder)
# Build command
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 |
Use pyinstaller --onefile for simple distribution. Build from a clean virtual environment with minimal dependencies to reduce file size. Always test the executable on a machine without Python installed.