Skip to main content

pyappdist

Build native installers for Python apps without hunting down hidden imports, data files, or plugins.

pyappdist packages your Python application into a native installer:

  • Windows: MSI / MSIX
  • macOS: .app bundle / DMG (notarization supported), or a self-extracting .run installer
  • Linux: Self-extracting .run installer

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. C extensions, abi3 wheels, Qt plugins, and tkinter-based GUIs work unmodified because the install layout is real.

Shipping a full runtime without selecting files makes packages larger, but against modern storage sizes that is a favorable trade for an environment that just runs.

📖 Documentation: https://pyappdist.readthedocs.io/

Status

Alpha — the pipeline works end-to-end, but expect breaking changes to the config schema, CLI, and output layout as it matures.

What it produces

One pyproject.toml can describe several output packages — each is a [[tool.pyappdist.targets]] entry with its own platform and format:

format Platform Output
msi windows-x86_64 .msi installer (per-user or machine-wide)
msix windows-x86_64 .msix package for the Microsoft Store / sideloading
linux linux-x86_64 self-extracting .run installer (per-user, no root)
macos macos-aarch64 / macos-x86_64 self-extracting .run installer (per-user, no root)
dmg macos-aarch64 / macos-x86_64 .dmg disk image (code-signing / notarization supported)
macapp macos-aarch64 / macos-x86_64 .app bundle (code-signing / notarization supported)
image any of the above plain archive of the install tree — .zip (Windows) / .tar.gz (Linux, macOS), no installer

The image format skips the installer entirely: the run-in-place tree is archived as-is (with launchers, unless no-launcher = true), ready to extract and run — useful for deploying with your own tooling or unpacking into a container.

Quick start

Add a [tool.pyappdist] section to your app's pyproject.toml:

[tool.pyappdist]
name = "My App"
python = "3.12"

[[tool.pyappdist.launchers]]
name = "myapp"              # produces myapp.exe (or a shell wrapper on Linux/macOS)
entry = "myapp:main"        # module:callable
# gui = true                # use pythonw.exe (no console window) on Windows
# icon = { windows = "assets/app.ico" }   # per-OS launcher icon table
# args = "--serve"          # fixed leading arguments

[[tool.pyappdist.targets]]
name = "windows"
platform = "windows-x86_64"
format = "msi"
manufacturer = "Example Inc."
# scope = "user"            # "user" (default, no admin) or "machine" (Program Files)

Then add pyappdist and build:

uv add --dev pyappdist
uv run pyappdist build      # builds the sole target: wheels -> runtime -> image -> launcher -> wix -> MSI

The result lands under appdist/<target>/dist/.

Multiple targets

Declare several targets to ship more than one package from the same config:

[[tool.pyappdist.targets]]
name = "windows"
platform = "windows-x86_64"
format = "msi"
manufacturer = "Example Inc."

[[tool.pyappdist.targets]]
name = "linux"
platform = "linux-x86_64"
format = "linux"

[[tool.pyappdist.targets]]
name = "macos-arm"
platform = "macos-aarch64"
format = "macos"

When several targets are defined, build requires you to name the one(s) to build (so it doesn't build them all at once); the individual pipeline stages default to all targets:

uv run pyappdist build linux           # build just the "linux" target
uv run pyappdist build windows         # build the Windows MSI

Samples

Runnable example apps live under samples/, each with its own [tool.pyappdist] config. They double as smoke tests for tricky cases (C extensions, GUI stacks, data files, per-target extras):

Sample Kind What it shows
helloworld CLI Smallest possible config — no dependencies. A good starting template; builds for every format (msi/msix/linux/macos/dmg).
pandascli CLI pandas + numpy (C extensions) collected as binary wheels and installed into the runtime. Console launcher (gui = false).
datafiles CLI Ships a bundled data file (data/ebi.jpeg) via [tool.uv.build-backend].data and reads it through sysconfig; opens it with Pillow.
multiprocessingdemo CLI Runs a multiprocessing.Pool across worker processes. The launcher runs the bundled interpreter directly, so spawn re-launches it correctly — no dependencies.
pytorchdemo CLI Ships PyTorch built for CUDA 13 (cu130) via a per-index [tool.uv.sources] pin; runs on the GPU when available, CPU otherwise. Windows/Linux only.
matplotlibdemo GUI matplotlib plot with the TkAgg backend — uses the runtime's bundled tkinter/tcl-tk, no extra GUI deps.
pygamedemo GUI A bouncing ball with pygame-ce (C extensions) collected as Windows wheels.
pyside6demo GUI A Qt window with PySide6 — a large abi3 wheel (cp39-abi3) installed into the cp312 runtime, Qt plugins and all.
niceguidemo GUI (web) "Weather Panel" built with NiceGUI + pywebview + requests; uses per-target extras (gtk/qt/gui) to pick the webview backend per platform.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyappdist-0.10.0.tar.gz (64.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyappdist-0.10.0-py3-none-any.whl (83.5 kB view details)

Uploaded Python 3

File details

Details for the file pyappdist-0.10.0.tar.gz.

File metadata

  • Download URL: pyappdist-0.10.0.tar.gz
  • Upload date:
  • Size: 64.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pyappdist-0.10.0.tar.gz
Algorithm Hash digest
SHA256 e1c6e0408202c10bba71c6e5d7200479f8cfee03dd80c3d64cfae29b8e632b2f
MD5 d01a4c3d82306723ddf078a3d25b1151
BLAKE2b-256 95c873fad54591eb4ad08ad55ce775d84c5f95525215c86ce9a6bf1035f58b72

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyappdist-0.10.0.tar.gz:

Publisher: publish.yml on atsuoishimoto/pyappdist

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyappdist-0.10.0-py3-none-any.whl.

File metadata

  • Download URL: pyappdist-0.10.0-py3-none-any.whl
  • Upload date:
  • Size: 83.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pyappdist-0.10.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0124216188d61412ff1690f76026b7c9d1054ce3eecac301ec06aae12ceced75
MD5 6f617da097c89da394a907e6666ef346
BLAKE2b-256 df50129004e220917b0bb97aaffa846309c0749b3bca5bdf776dceb30cfa925c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyappdist-0.10.0-py3-none-any.whl:

Publisher: publish.yml on atsuoishimoto/pyappdist

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page