# PyInstaller

PyInstaller is a Python [application](https://pydevtools.com/handbook/explanation/what-is-a-python-application.md) bundler that freezes a Python program, the CPython interpreter it was built against, and every imported dependency into a single distributable. The output runs on end-user machines without a pre-installed Python interpreter and without a [virtual environment](https://pydevtools.com/handbook/explanation/what-is-a-virtual-environment.md). PyInstaller supports CPython on macOS, Linux, and Windows; there is no iOS, Android, or tvOS target. Install it with `uv tool install pyinstaller` or `pipx install pyinstaller`.

## Key Features

- One-folder bundles via `--onedir` and self-extracting single-file executables via `--onefile`
- Hook system that declares hidden imports, data files, and runtime patches for third-party packages
- Community hook repository (`pyinstaller-hooks-contrib`) installed alongside PyInstaller, covering packages such as NumPy, SciPy, PyQt, and Matplotlib
- Spec file workflow for declarative builds (`pyinstaller my.spec`) in addition to CLI flags
- `--exclude-module` flag for trimming unused transitive dependencies out of a bundle
- Optional UPX compression for smaller executables

## Build Process

A PyInstaller spec file orchestrates four stages: `Analysis` walks the entry-point script's imports to produce a dependency graph and applies hooks for packages that need special handling. `PYZ` compiles pure-Python modules to bytecode and packs them into a ZlibArchive. `EXE` concatenates the PYZ, extension modules, shared libraries, and data files (wrapped internally in a `PKG` CArchive) onto a precompiled native bootloader and writes the executable. `COLLECT` copies the executable plus any external dependencies into the output folder for one-folder builds; one-file builds skip `COLLECT` and emit the self-extracting executable directly.

The bootloader is a small C program that, at runtime, extracts or memory-maps the PYZ archive, initializes the embedded Python interpreter, and executes the frozen entry point. PyInstaller ships precompiled bootloaders for supported platforms; the bootloader source is available in the project repository for teams that need to recompile it.

## Distribution Modes

The `--onedir` mode produces a folder containing the executable, the Python interpreter shared library, and every dependency file. Startup is fast because no archive extraction happens at launch.

The `--onefile` mode concatenates the same contents into a self-extracting archive. At launch, the bootloader extracts the payload into a temporary directory, runs the program, and cleans up on exit. Startup is slower than `--onedir` and the temporary directory can be inspected by anyone with filesystem access.

## Pros

- Extensive third-party hook coverage via `pyinstaller-hooks-contrib`, reducing manual work for packages with C extensions or data files
- Cross-platform support across macOS, Linux, and Windows from the same spec file
- Active maintenance with regular releases
- Licensed GPLv2-or-later with a documented exception that permits distributing bundled applications under any license, including proprietary ones
- Works with any CPython installation; does not require a specific build backend or project layout

## Cons

- Bundle sizes for ML workloads are large. Community reports for PyTorch and TensorFlow `--onefile` executables range from roughly 500 MB to 1.5 GB before manual `--exclude-module` tuning.
- No built-in code signing. Windows distributions require `signtool` with a code-signing certificate; macOS distributions require `codesign` and `notarytool` with an Apple Developer ID.
- Antivirus false positives are a known issue. PyInstaller documents rebuilding the bootloader from source and signing the result as a mitigation, which requires a C toolchain on each target platform.
- `--onefile` startup is slower than `--onedir` because the payload is extracted on every launch.
- No mobile target support. Projects shipping to iOS or Android need a different tool such as [Briefcase](https://pydevtools.com/handbook/reference/briefcase.md).
- Bundles the interpreter at build time, so the build machine's OS and architecture must match the target. Cross-compilation is not supported.

## Alternatives

[Nuitka](https://pydevtools.com/handbook/reference/nuitka.md) compiles Python source to C and produces a native binary instead of freezing bytecode. [cx_Freeze](https://pydevtools.com/handbook/reference/cx-freeze.md) provides a lighter-weight freezing workflow for pure-Python programs without heavy third-party dependencies. [Briefcase](https://pydevtools.com/handbook/reference/briefcase.md) targets native installer formats and app stores on desktop and mobile platforms.

`auto-py-to-exe` is a third-party GUI wrapper around PyInstaller. It exposes the PyInstaller CLI flags as form inputs and is maintained independently of PyInstaller itself.

## Learn More

- [PyInstaller documentation](https://pyinstaller.org/en/stable/)
- [PyInstaller GitHub repository](https://github.com/pyinstaller/pyinstaller)
- [pyinstaller-hooks-contrib](https://github.com/pyinstaller/pyinstaller-hooks-contrib) provides community-maintained hooks for third-party packages
- [auto-py-to-exe](https://github.com/brentvollebregt/auto-py-to-exe) is a GUI wrapper around PyInstaller
- [Nuitka](https://pydevtools.com/handbook/reference/nuitka.md) compiles Python to C instead of freezing bytecode
- [cx_Freeze](https://pydevtools.com/handbook/reference/cx-freeze.md) is a freezer with narrower hook coverage
- [Briefcase](https://pydevtools.com/handbook/reference/briefcase.md) targets native installer formats and mobile platforms
- [How do I ship a Python application to end users?](https://pydevtools.com/handbook/explanation/how-do-i-ship-a-python-application-to-end-users.md) compares the freezer options with `uvx`, PyApp, and ONNX
- [What is a Python application?](https://pydevtools.com/handbook/explanation/what-is-a-python-application.md)
