Have you ever created an amazing tool or script in Python but couldn't share it due to the code being interpreted, having lots of dependencies, or you didn't want to give away your private API keys or other secrets?
Here is how to compile your Python app into a standalone executable for Mac, Windows, and Linux using PyInstaller.
We will ensure sensitive data like API keys are securely bundled and will even address problems like what to do about additional data files.
Getting Started
- You'll need Python 3.7+ installed.
- A Python app (e.g.,
myapp.py
) that runs locally. For our example we will use a basic console app that gets the current weather.
Ensure your app works locally (python3 myapp.py
).
If it uses API keys, like our example does, then store them securely in a .env
file to avoid hardcoding.
Example: MyApp.py
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("WEATHER_API_KEY")
if not API_KEY:
raise ValueError("API key not found.")
def fetch_weather(city):
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}&units=metric"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return f"Temperature: {data['main']['temp']}°C, Weather: {data['weather'][0]['description']}"
except requests.RequestException as e:
return f"Error: {e}"
if __name__ == "__main__":
city = input("Enter city: ")
print(fetch_weather(city))
Create .env
Create a .env
file and make sure it is in the same directory as your Python (eg. ./myapp.py
):
WEATHER_API_KEY=your_actual_api_key_here
Next you will need to
- Install
python-dotenv
:pip3 install python-dotenv
. - If using git, add
.env
to.gitignore
to prevent accidentally sharing.
PyInstaller
Install PyInstaller using Pip, which we will use to compile your app:
pip3 install pyinstaller
Try pyinstaller --version
to make sure it is installed ok.
If you run pyinstaller myapp.py
it WILL compile, but you will get a bunch of weird or unwanted files. So we need a small extra step.
How to Compile to a Single Executable
To create a single, standalone binary (avoiding extra files/folders), use the --onefile
option. Include the .env
file for API keys.
Run:
pyinstaller --onefile --add-data ".env;." --name MyApp myapp.py
- Options:
--onefile
: Bundles everything into one executable.--add-data ".env;."
: Includes the.env
file (use:
for Mac/Linux,;
or^;
for Windows).--name MyApp
: Names the binaryMyApp
(e.g.,MyApp.exe
on Windows).
- Output:
dist/MyApp
(ordist/MyApp.exe
): The executable for distribution.build/
andmyapp.spec
: Temporary files (delete after building).
Testing the Compiled Executable
- Run the binary:
- Mac/Linux:
./dist/MyApp
- Windows:
dist\MyApp.exe
- Mac/Linux:
- Verify it works (e.g., prompts for a city and shows weather data).
- To check API key security look for the
.env
, instead of being a file in the directory it should now be bundled into the binary file, and the key should be hidden.
Distribute for Other Platforms
To distribute for Mac, Windows, and Linux:
- Native Builds (recommended):
- Mac: Run PyInstaller on macOS.
- Windows: Run on Windows.
- Linux: Run on Linux (ensure OpenGL support:
sudo apt-get install libgl1-mesa-glx
on Ubuntu).
- Cross-Compilation (advanced):
- Use virtual machines (e.g., VirtualBox for Windows) or Docker.
- Test binaries on each platform.
- Install dependencies (e.g.,
requests
,python-dotenv
) on the build machine.
Cleaning Up
- Keep:
dist/MyApp
(the binary). - Delete:
build/
,myapp.spec
, and anydist/myapp/
folder. - Distribute: Share the binary with users for their chosen platform.
Troubleshooting
- Binary Doesn’t Run:
- Add missing libraries to
myapp.spec
(edithiddenimports
):
Re-run:hiddenimports=['requests', 'python-dotenv']
pyinstaller myapp.spec
. - Check terminal errors (run binary in a terminal).
- Add missing libraries to
.env
Not Found:- Verify
--add-data ".env;."
andload_dotenv()
inmyapp.py
and ensure you use:
and not;
on Mac/Linux.
- Verify
Large Binary (~20-50MB):
It is normal for --onefile
to produce large files. We can reduce that down with UPX: pyinstaller --onefile --upx-dir /path/to/upx myapp.py
.
UPX (Ultimate Packer for Executables) is a free, open-source tool that compresses executable files, such as those generated by PyInstaller, to reduce their file size. In the context of your goal to compile a Python textual app (myapp.py) into a single executable for cross-platform distribution (Mac, Windows, Linux) with secure API key handling, UPX can help make your PyInstaller-generated binaries smaller, improving distribution efficiency.
Benefits:
- Smaller file sizes (e.g., a 50MB PyInstaller binary might shrink to 20-30MB).
- Faster downloads/sharing for users.
- No runtime performance penalty (decompression is fast).
Drawbacks:
- Compression may slightly increase startup time (negligible in most cases).
- Some antivirus software may flag UPX-compressed binaries as suspicious (less common with modern versions).
- Not all executables compress well (depends on content).
Security Tips
- API Keys: The
.env
file keeps keys secure in the binary. - Obfuscation: Use Cython for extra protection:
pip install cython cythonize -i myapp.py
- Encryption (advanced): Encrypt keys and decrypt at runtime:
from cryptography.fernet import Fernet key = Fernet.generate_key() # Save securely cipher = Fernet(key) encrypted_key = cipher.encrypt(API_KEY.encode()) # Decrypt: API_KEY = cipher.decrypt(encrypted_key).decode()
Mobile Apps:
- Use Buildozer to package Kivy apps for iOS/Android.
- Include
.env
inbuildozer.spec
(source.include_exts = py,env
).
Thanks for your contribution to the STEMsocial community. Feel free to join us on discord to get to know the rest of us!
Please consider delegating to the @stemsocial account (85% of the curation rewards are returned).
You may also include @stemsocial as a beneficiary of the rewards of this post to get a stronger support.