Skip to main content

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
Icon Format

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

FlagShortPurpose
--onefile-FBundle everything into single EXE
--noconsole-wHide console window (GUI apps)
--console-cShow console (default)
--icon=file.ico-iSet application icon
--name=AppName-nCustom executable name
--noconfirm-yOverwrite without asking
--cleanClean cache before building
--add-dataInclude 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

Platform Limitation

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:

  1. Digitally sign your executable
  2. Submit for analysis at Microsoft Security Intelligence
  3. 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

ToolProsCons
PyInstallerMost popular, well-documentedLarge file size
cx_FreezeCross-platformMore configuration needed
NuitkaCompiles to C, faster executionLonger build time
py2exeWindows-focusedWindows 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

ScenarioCommand
Basic EXEpyinstaller --onefile script.py
GUI apppyinstaller -F -w app.py
With iconpyinstaller -F -w -i icon.ico app.py
With datapyinstaller -F --add-data "data;data" app.py
Debug buildpyinstaller -F -c --debug=all app.py
Best Practice

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.