pyappdist
Turn a Python app into a native installer — and it just works.
Warning
Alpha. pyappdist is under active development. It works end-to-end today, but the config schema, CLI, and output layout may still change without notice.
pyappdist does not freeze your code. Instead of bundling Python and your app
into a single executable (and fighting hidden imports, data files, and plugins
along the way), it installs your app into a real, dedicated Python runtime —
exactly the way pip would — and ships that.
Because the runtime is a normal Python environment, most apps run as-is: no
hooks, no --hidden-import, no --add-data, no per-library workarounds. If
it runs under uv run, it almost certainly runs after pyappdist build.
Output formats
One pyproject.toml can describe several output packages at once — each is a
target with its own platform and format:
- msi
windows-x86_64→.msiinstaller + portable.zip.- msix
windows-x86_64→.msix(Store / sideloading).- linux
linux-x86_64→.tar.{gz,bz2,xz}+.runinstaller.- macos
macos-aarch64/macos-x86_64→.tar.{gz,bz2,xz}+.runinstaller.- macapp / dmg
macos-aarch64/macos-x86_64→ a signed/notarized.appbundle, optionally inside a.dmg.
Why “just works”
Freezers reconstruct your app by static analysis, so anything dynamic tends to break and needs manual patching. pyappdist skips all of that:
Real install layout —
dist-info, entry points,.pthfiles, and package data are exactly where the package authors put them.importlib.metadata/importlib.resourcesbehave identically to a normal install.Real binary wheels — C extensions and platform wheels (incl.
abi3) are installed unmodified, with the real interpreter and DLL search paths. No bundling guesswork.Real GUI stacks — Qt plugins ship in PySide6’s normal wheel layout; matplotlib’s TkAgg backend uses the runtime’s bundled tkinter. They just load.
On Windows the launcher is a tiny C stub that starts the bundled python.exe /
pythonw.exe as a subprocess, so there is no pythonXX.dll embedding and no
C-API version risk — the stub never changes when the Python version does. On
Linux/macOS the launcher is a relocatable shell wrapper. See How it works.
Getting started
Output formats
Status
Alpha — the pipeline works end-to-end, but expect breaking changes to the config schema, CLI, and output layout as it matures.
Targets today are Windows x64 (msi, msix), Linux x64 (linux), and
macOS arm64/x64 (macos). Auto-update and code-signing certificates are out of
scope for now; optional Code signing of the Windows artifacts is available.
Distributed apps are not obfuscated, and unsigned Windows installers will trigger a
SmartScreen warning.