Skip to main content

Turn a Python app into a native installer (Windows MSI/MSIX, Linux, macOS)

Project description

pyappdist

Turn a Python app into a native installer — and it just works.

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

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

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) + portable .zip
msix windows-x86_64 .msix package for the Microsoft Store / sideloading
linux linux-x86_64 .tar.gz + self-extracting .run installer (per-user, no root)
macos macos-aarch64 / macos-x86_64 .tar.gz + self-extracting .run installer (per-user, no root)

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 = "assets/app.ico"   # launcher icon
# 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-x86_64  # build the Windows MSI

CLI

build runs the whole pipeline; each stage is also its own subcommand if you need to run them individually (each takes an optional project dir via -C, default .):

uv run pyappdist fetch-runtime    # download the python-build-standalone runtime
uv run pyappdist build-wheels     # app + dependency wheels -> <target>/wheelhouse
uv run pyappdist build-image      # runtime + pip install + compileall -> <target>/image
uv run pyappdist build-launchers  # compile launcher(s) into the image (Windows, MSVC)
uv run pyappdist gen-wix          # scan the image -> WiX .wxs
uv run pyappdist build            # all of the above -> the package(s) in <target>/dist

Samples

Runnable example apps live under samples/, each with its own [tool.pyappdist] config:

  • helloworld — minimal console app (msi + linux + macos targets)
  • datafiles — bundled package data / resources
  • pandascli — a CLI built on a C-extension dependency
  • matplotlibdemo — matplotlib's TkAgg backend via the runtime's tkinter
  • pygamedemo — a pygame GUI
  • pyside6demo — a PySide6 (Qt) GUI, plugins and all

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 signing of the Windows artifacts is available via PYAPPDIST_SIGN_CMD (docs). Distributed apps are not obfuscated, and unsigned Windows installers will trigger a SmartScreen warning.

Project details


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.3.0.tar.gz (50.2 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.3.0-py3-none-any.whl (68.3 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for pyappdist-0.3.0.tar.gz
Algorithm Hash digest
SHA256 a47ed131f20a53c79e2b9818b79c2d4a5ead463b3b9fc8da673032fba34a1e29
MD5 e9e137bb7ffd8c063762e1378e43fca2
BLAKE2b-256 f6af51d7d7d5af9cd3009c4f80620dd572c6dc27bb9bef35ab8110f991bf15d6

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyappdist-0.3.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.3.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for pyappdist-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 560068d0bcb5cdb313bf8a293f16d88b8c8d710b0ae7e4d231d05f5e8aa1239a
MD5 a5648c7be8e5db9c54614c01efc6a99d
BLAKE2b-256 4347150212bced4a25d8432d04e72e88a3014169fe2a81f0e62f883eff2bcbe7

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyappdist-0.3.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