Package Python projects into executables
pyfuze
Description
pyfuze packages your Python project into one single executable.
This project is primarily built on top of cosmopolitan and uv.
Packaging Modes
| Mode | Offline | Cross-Platform | Size | Compatibility | |------------------------|------------|----------------|------------|----------------| | Bundle (default) | β | β | π΄ Large | π’ High | | Online | β | β | π’ Small | π’ High | | Portable | β | β | π‘ Medium | π΄ Low |
Bundle mode packages your application with Python and all dependencies included. It only runs on the same platform it was packaged on, providing the highest compatibility. The package extracts its contents to --unzip-path at runtime.
Online mode produces a smaller, cross-platform package. At runtime, it extracts the package and downloads necessary dependencies to --unzip-path (internet connection required). This keeps the package lightweight and adaptable across different systems.
Portable mode creates a standalone, cross-platform executable that requires no extraction or internet connection. It supports only pure Python projects and dependencies. This mode is based on python.com from the cosmos-4.0.2.zip release, currently fixed to Python version 3.12.3.
Cross-Platform Support
The cross-platform capability of the online mode is mainly limited by uv: https://docs.astral.sh/uv/reference/policies/platforms/
The portable modeβs cross-platform support depends on the APE specification itself: https://github.com/jart/cosmopolitan/blob/master/ape/specification.md
In summary, both modes are expected to run on macOS (ARM64 and AMD64), Linux (AMD64), and Windows (AMD64).
Install
pip install pyfuze
Alternatively, you can run it directly with uvx:
uvx pyfuze -h
Usage
Usage: pyfuze [OPTIONS] PYTHON_PROJECT
Package Python projects into executables.
Options: --mode TEXT Available modes:
- bundle(default): Includes Python and all dependencies. Runs only on the same platform. Highest compatibility. Extracts to --unzip-path at runtime.
- online: Small and cross-platform. Extracts and downloads dependencies to --unzip-path at runtime (requires internet).
- portable: Standalone cross-platform executable. No extraction and internet needed. Supports only pure Python and --output-name, --entry, --reqs, --include, --exclude.
--output-name TEXT Output executable name [default: <project_name>.com] --entry TEXT Entry Python file. Used when your project is a folder. [default: main.py] --reqs TEXT Add requirements.txt file to specify dependencies (input comma-separated string OR file path) --include TEXT Include extra files or folders (e.g. config.ini) (source[::destination]) (repeatable) --exclude TEXT Exclude project files or folders (e.g. build.py) (repeatable). --unzip-path TEXT Unzip path for bundle and online modes (default: /tmp/<project_name>) --python TEXT Add .python-version file to specify Python version (e.g. 3.11) --pyproject FILE Include pyproject.toml to specify project dependencies --uv-lock FILE Include uv.lock file to lock dependencies --win-gui Hide the console window on Windows --env TEXT Add environment variables such as INSTALLERDOWNLOADURL, UVPYTHONINSTALL_MIRROR and UVDEFAULTINDEX (key=value) (repeatable) --uv-install-script-windows TEXT UV installation script URI for Windows (URL or local path) [default: https://astral.sh/uv/install.ps1] --uv-install-script-unix TEXT UV installation script URI for Unix (URL or local path) [default: https://astral.sh/uv/install.sh] -d, --debug Enable debug logging -v, --version Show the version and exit. -h, --help Show this message and exit.
Examples
Portable Mode
Use portable mode to generate a standalone, cross-platform executable. Best suited for simple scripts like simple.py.
pyfuze ./examples/simple.py --mode portable --reqs requests
Bundle Mode
This command generates complex.com in the dist folder using the bundle mode. Since complex is a folder-based project, we specify the entry file using --entry.
We include pyproject.toml and uv.lock via --pyproject and --uv-lock to define the project's dependencies.
By default, pyfuze packages all Python files and packages (i.e. folders with init.py) recursively in the project directory.
The --include flag adds extra files like config.txt, while --exclude skips files such as build.py.
The default extraction path is /tmp/complex, but here we set it to a local ./complex directory using --unzip-path. This is useful if you want to preserve files like config.txt across reboots, since temporary directories may be cleared. Alternatively, you can have your Python script copy config.txt to a persistent location, such as %LocalAppData% on Windows.
Finally, the --win-gui option hides the console window on Windows, making it ideal for GUI applications.
pyfuze ./examples/complex \
--entry app.py \
--pyproject ./examples/complex/pyproject.toml \
--uv-lock ./examples/complex/uv.lock \
--include ./examples/complex/config.txt \
--exclude ./examples/complex/build.py \
--unzip-path complex \
--win-gui
Online Mode
Use online mode to generate a smaller, cross-platform package. It requires an internet connection to download Python and dependencies at runtime.
To improve reliability in restricted networks, you can use --uv-install-script-windows, --uv-install-script-unix, and --env to specify mirror URLs.
pyfuze ./examples/complex \
--entry app.py \
--pyproject ./examples/complex/pyproject.toml \
--uv-lock ./examples/complex/uv.lock \
--include ./examples/complex/config.txt \
--exclude ./examples/complex/build.py \
--unzip-path complex \
--win-gui \
--mode online \
--uv-install-script-windows <uv-windows-installer-mirror-url> \
--uv-install-script-unix <uv-unix-installer-mirror-url> \
--env INSTALLERDOWNLOADURL=<uv-binary-mirror-url> \
--env UVPYTHONINSTALL_MIRROR=<python-install-mirror-url> \
--env UVDEFAULTINDEX=<pypi-mirror-url>
Working Directory
The default working directory is <unzip-path>/src.
If you want to switch to the directory where the pyfuze executable resides, you can use the PYFUZEEXECUTABLEPATH environment variable:
import os
os.chdir(os.path.dirname(os.environ["PYFUZEEXECUTABLEPATH"]))
If you want to switch to the directory where the user invoked the executable, you can use the PYFUZEINVOKEDIR environment variable:
import os
os.chdir(os.environ["PYFUZEINVOKEDIR"])
Note
pyfuze does NOT perform any kind of code encryption or obfuscation.
License
pyfuze is released under the MIT license.
Contacts
- Report bugs and feature requests via GitHub Issues
- Discuss on Discord
- QQ Group: 1054869699