Python to EXE: Complete Guide 2026 (Nuitka, PyInstaller, Prometheus Shield)
You have written a Python application and you want to distribute it as a standalone Windows executable. No Python installation required. No pip dependencies. Just a .exe that users double-click and it works. This is one of the most common challenges in the Python ecosystem, and the landscape of solutions in 2026 has both improved and become more complex.
This guide covers every viable method, explains the trade-offs of each, and walks through the process with real code examples. We will pay special attention to the antivirus problem — the reason most Python-to-EXE solutions trigger false positives — and how to solve it.
The Methods: Quick Comparison
| Tool | Approach | AV Safe | Speed | Size | Source Protection |
|---|---|---|---|---|---|
| Nuitka | C compilation | Yes | Fastest | Medium | Strong |
| PyInstaller | Bundled interpreter | No | Moderate | Large | Weak |
| cx_Freeze | Bundled interpreter | No | Moderate | Large | Weak |
| py2exe | Bundled interpreter | No | Moderate | Large | Weak |
| Prometheus Shield | Automated pipeline | Yes | Fast | Optimized | Maximum |
Method 1: PyInstaller
PyInstaller is the most popular Python-to-EXE tool, and for good reason — it handles most use cases with minimal configuration. The basic usage is straightforward:
pip install pyinstaller
pyinstaller --onefile --windowed my_app.py
This produces a single .exe file in the dist/ directory. The --onefile flag bundles everything into one file (at the cost of slower startup, since it must extract to a temp directory). The --windowed flag suppresses the console window for GUI applications.
How PyInstaller Works
PyInstaller does not compile your Python code. Instead, it:
- Bundles the Python interpreter (python3x.dll) into the executable
- Bundles all imported Python modules as .pyc bytecode files
- Bundles all dependency DLLs and data files
- Creates a bootloader that unpacks everything and runs your script with the bundled interpreter
This means your code still runs as interpreted Python — it is just self-contained. The .exe is essentially a ZIP archive with a custom extractor.
The Antivirus Problem
Here is why this happens. PyInstaller's bootloader uses techniques that are also common in actual malware:
- Self-extracting archives — The EXE unpacks files to a temp directory at runtime. Malware does the same thing to evade static analysis.
- Dynamic code loading — The bootloader loads and executes Python bytecode from the extracted files. This pattern triggers heuristic detection.
- Packed/compressed payloads — The bundled modules are compressed inside the EXE, making static analysis difficult. AV engines are suspicious of executables with large compressed payloads.
- Known signatures — Because PyInstaller is so popular, AV vendors have fingerprinted its bootloader. Some AV engines flag any PyInstaller executable by default and wait for community reports to whitelist specific ones.
In practice, a freshly built PyInstaller executable will trigger false positives on 5-15 out of 70+ AV engines on VirusTotal. This is devastating for commercial software distribution. Users see a malware warning and immediately delete your application.
PyInstaller Verdict
Use for: Internal tools, personal scripts, prototyping. Situations where you control the deployment environment and can whitelist the executable.
Do not use for: Commercial distribution, anything users download from the internet, applications where trust matters.
Method 2: Nuitka (Recommended)
Nuitka takes a fundamentally different approach: it compiles your Python code to C, then compiles the C code to a native executable using a standard C compiler (MSVC on Windows, GCC on Linux). The result is genuine machine code, not a bundled interpreter running bytecode.
pip install nuitka
python -m nuitka --standalone --onefile --windows-console-mode=disable my_app.py
How Nuitka Works
- Python to C translation — Nuitka analyzes your Python source code and generates equivalent C source code. Each Python function becomes a C function. Python objects become C structs with reference counting.
- C compilation — The generated C code is compiled by MSVC (Microsoft Visual C++) or another C compiler into native machine code. This is real compilation — the output is an executable with native CPU instructions, not bytecode.
- Runtime linking — The compiled code links against the CPython runtime (libpython) for operations that require the interpreter (dynamic typing, garbage collection). This is a significantly smaller footprint than bundling the entire interpreter.
- Dependency bundling — For
--standalonemode, Nuitka bundles necessary DLLs and packages alongside the executable.
Why Nuitka Avoids AV False Positives
Because the output is a standard compiled C program, it looks like any other native application to antivirus software. There is no self-extracting archive, no bootloader, no dynamic bytecode loading. The executable's structure is identical to software written in C or C++ from scratch. AV engines have no reason to flag it.
In our testing across four commercial products (BeatSync PRO, Clareon, NEXUS AI, Prometheus Shield), Nuitka-compiled executables consistently achieve zero false positives on VirusTotal. This is the single most important advantage of Nuitka for commercial software distribution.
Performance Benefits
Nuitka-compiled code is typically 10-30% faster than interpreted Python for CPU-bound operations. The C compiler applies standard optimizations (loop unrolling, function inlining, register allocation) that the Python interpreter cannot. For I/O-bound code, the improvement is minimal since the bottleneck is not CPU execution.
Nuitka Setup on Windows
Nuitka requires a C compiler. On Windows, you need either:
- MSVC (Visual Studio Build Tools) — The recommended option. Install "Desktop development with C++" workload from Visual Studio Installer. This gives you
cl.exe. - MinGW — An alternative if you do not want the full Visual Studio installation. Nuitka can download this automatically.
# Install Nuitka
pip install nuitka
# Verify C compiler is available
python -m nuitka --version
# Build standalone executable
python -m nuitka --standalone --onefile ^
--windows-icon-from-ico=icon.ico ^
--windows-console-mode=disable ^
--include-data-dir=assets=assets ^
--output-dir=build ^
my_app.py
Key flags explained:
--standalone— Include all dependencies (no Python installation needed on target machine)--onefile— Pack everything into a single executable--windows-icon-from-ico— Embed a custom icon--windows-console-mode=disable— No console window (for GUI apps)--include-data-dir— Bundle data files (images, configs, models)
Nuitka Build Times
Nuitka builds are slower than PyInstaller because actual C compilation happens. Typical times on a modern system:
- Small script (100 lines): 30-60 seconds
- Medium application (5,000 lines): 3-8 minutes
- Large application (50,000+ lines with many dependencies): 15-45 minutes
Build time is a one-time cost per release. The AV-clean output is worth the wait.
Method 3: cx_Freeze
cx_Freeze is similar to PyInstaller in approach (bundled interpreter) but with a different configuration model based on setup.py. It is slightly less prone to AV false positives than PyInstaller but still uses the same fundamental pattern.
# setup.py
from cx_Freeze import setup, Executable
setup(
name="MyApp",
version="1.0",
executables=[Executable("my_app.py", base="Win32GUI")]
)
pip install cx_Freeze
python setup.py build
cx_Freeze is a viable option for internal distribution where AV is not a concern, but it shares the same fundamental limitations as PyInstaller for commercial use.
Method 4: The Native Launcher Architecture
After experiencing AV quarantine issues across four commercial products, we developed a hybrid approach that combines Nuitka compilation with a native C launcher. This is the architecture used in BeatSync PRO, Clareon, NEXUS AI, and Prometheus Shield.
The Architecture
- Native C launcher (100-180KB) — A minimal C program compiled with MSVC. This is the file users double-click. It has the product icon embedded via
rc.exe(Windows Resource Compiler). The launcher does one thing: spawn the actual application executable in the_internal/subdirectory. - Nuitka-compiled application — The real application, compiled with Nuitka, lives in the
_internal/directory alongside its dependencies.
Why this works:
- The launcher is a standard C executable — zero AV flags
- The Nuitka executable in
_internal/is also standard compiled code — zero AV flags - No self-extracting, no bytecode loading, no bootloader patterns
- The launcher is tiny (under 200KB) so antivirus scans complete instantly
- Icon and version information are embedded natively in the launcher, not injected post-build
Building the Native Launcher
Here is a simplified version of the launcher template:
// launcher.c
#include <windows.h>
#include <stdio.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
char path[MAX_PATH];
char dir[MAX_PATH];
// Get directory of this launcher
GetModuleFileNameA(NULL, path, MAX_PATH);
char *lastSlash = strrchr(path, '\\');
if (lastSlash) {
size_t dirLen = lastSlash - path;
strncpy(dir, path, dirLen);
dir[dirLen] = '\0';
}
// Build path to actual executable
char exePath[MAX_PATH];
snprintf(exePath, MAX_PATH, "%s\\_internal\\MyApp.exe", dir);
// Launch it
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi;
if (CreateProcessA(exePath, NULL, NULL, NULL,
FALSE, 0, NULL, dir, &si, &pi)) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return 0;
}
Compile with MSVC:
rc /fo launcher.res launcher.rc
cl /O2 launcher.c launcher.res /link /OUT:MyApp.exe
/SUBSYSTEM:WINDOWS user32.lib kernel32.lib
The .rc file embeds the icon and version information:
// launcher.rc
1 ICON "app_icon.ico"
1 VERSIONINFO
FILEVERSION 1,0,0,0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "FileDescription", "MyApp"
VALUE "ProductName", "MyApp"
VALUE "CompanyName", "My Company"
VALUE "FileVersion", "1.0.0"
END
END
END
Method 5: Prometheus Shield (Automated Pipeline)
Prometheus Shield automates the entire build pipeline described above. Instead of manually configuring Nuitka, writing C launchers, compiling with MSVC, and running QA checks, you feed your Python project into Prometheus Shield and it handles the 12-step pipeline automatically:
- Source analysis — 30 QA agents scan your code for issues that will cause problems during compilation
- Dependency resolution — Automatically identifies all imports, including hidden imports that Nuitka might miss
- Asset collection — Gathers data files, images, models, configs
- Nuitka compilation — Compiles Python to C to native code with optimized flags
- Native launcher generation — Creates the C launcher with embedded icon and version info
- MSVC compilation — Compiles the launcher
- Directory structure — Assembles the final distribution folder
- Legal document generation — Creates EULA, license, and README files
- QA scanning — Agents verify the build for PyInstaller markers, missing DLLs, broken imports
- AV pre-check — Verifies the output would not trigger common AV heuristics
- Size optimization — Strips unnecessary files from the distribution
- ZIP packaging — Creates the final distributable archive
This pipeline was developed out of necessity after manually building four commercial products and encountering every possible failure mode. The QA agents alone have saved hundreds of hours by catching issues (missing DLLs, incorrect icon formats, PyInstaller artifact leftovers) before they reach end users.
Common Pitfalls and Solutions
Hidden Imports
Both PyInstaller and Nuitka can miss dynamic imports that are not visible through static analysis:
# This import is visible to static analysis
import json
# This import is NOT visible to static analysis
module = __import__(f"plugins.{plugin_name}")
# Neither is this
importlib.import_module(f"handlers.{handler_type}")
Solution: explicitly declare hidden imports in your build configuration:
# Nuitka
python -m nuitka --include-module=plugins.audio ^
--include-module=plugins.video ^
--include-package=handlers ^
my_app.py
Data Files and Assets
Images, model files, configuration files, and other non-Python assets need to be explicitly included:
# Nuitka
python -m nuitka --include-data-dir=models=models ^
--include-data-files=config.json=config.json ^
my_app.py
In your code, reference data files relative to the executable, not the script:
import sys
import os
if getattr(sys, 'frozen', False):
# Running as compiled executable
base_dir = os.path.dirname(sys.executable)
else:
# Running as script
base_dir = os.path.dirname(os.path.abspath(__file__))
config_path = os.path.join(base_dir, 'config.json')
Large Dependencies
Packages like PyTorch, TensorFlow, or NumPy can bloat your executable to gigabytes. Strategies to manage this:
- Use
--nofollow-import-toin Nuitka to exclude unused submodules - Consider ONNX Runtime instead of full PyTorch for inference-only applications
- Distribute as a folder (skip
--onefile) to avoid the startup extraction penalty
Choosing the Right Method
Decision tree:
- Is this for commercial distribution? → Use Nuitka (or Prometheus Shield for full automation). The AV problem rules out PyInstaller.
- Is this an internal tool? → PyInstaller is fine. Fast to set up, well-documented, handles most cases.
- Do you need maximum source code protection? → Nuitka (compiled C is much harder to reverse-engineer than .pyc bytecode). See our guide on protecting Python source code.
- Do you need maximum performance? → Nuitka. The C compilation provides 10-30% speedup on CPU-bound code.
- Do you have a complex build with multiple products? → Prometheus Shield automates the entire pipeline with 30 QA agents.
The Python-to-EXE problem is solved in 2026. The right tools exist. The key is choosing the approach that matches your distribution requirements, not just the one that is fastest to set up.
Automate Your Build Pipeline
Prometheus Shield automates the entire Python-to-EXE pipeline: Nuitka compilation, native C launcher, 30 QA agents, AV-clean output.
Get Prometheus Shield