pyappdist
Ship your Python app as a native installer straight from pyproject.toml.
If it installs with pip, it almost certainly ships with pyappdist.
📖 Documentation: https://pyappdist.readthedocs.io/
pyappdist bridges the Python packaging ecosystem and native application
distribution. It reads your application's pyproject.toml and builds setup
packages for Windows, macOS, and Linux:
- Windows: MSI / MSIX
- macOS: DMG /
.appbundle, PKG installer, or a self-extracting installer - Linux: self-extracting installer
For example, to build a Windows MSI, configure pyproject.toml like this:
[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 build the MSI package (on Windows):
uvx pyappdist build
The result lands under appdist/<target>/dist/.
How it works
pyappdist creates a dedicated Python runtime directory and installs your
application and its dependencies into it with pip. That runtime directory
itself becomes the setup package. The Python runtime comes from
python-build-standalone,
the same distribution Astral's uv uses to build its environments.
With this approach, binary files such as a package's DLLs and related files
such as images are placed in the proper directories according to the Python
language specification and the PyPA specifications. Because this environment
is used for the setup package as-is, most applications can be expected to run
unmodified, with no per-application adjustments. If your app runs under
uv run, it almost certainly runs after pyappdist build.
Why pyappdist
Tools such as PyInstaller and Nuitka analyze your code, select only the necessary files from the Python interpreter and dependency packages, and build an executable or a directory from that minimal set of files.
The problem is that the selection is not always correct. Static analysis cannot reliably find dynamically imported modules, data files, or plugins, so these tools often need per-application adjustments — hidden-import declarations, data-file lists, and library-specific hooks — and adding a new dependency can break the build again.
Those tools trade complexity for smaller distributions. A typical Python runtime adds roughly 100–150 MB. That used to matter more than it does today.
pyappdist makes the opposite trade-off: it builds a complete environment according to the Python and PyPA specifications and creates the distribution package from it. What your application and its dependencies contain does not matter — there is nothing to hunt down and nothing to adjust per application. It also does not build a one-binary executable — it only provides small launcher executables where needed. Through this design, pyappdist creates a stable application environment that end users can rely on.
What it produces
Each [[tool.pyappdist.targets]] entry describes one output package,
selected by its platform and format:
format |
Platform | Output |
|---|---|---|
msi |
Windows | .msi installer |
msix |
Windows | .msix package (Microsoft Store / sideloading) |
linux |
Linux | self-extracting .run installer |
macos |
macOS | self-extracting .run installer |
macapp |
macOS | .app bundle |
dmg |
macOS | .dmg disk image |
pkg |
macOS | .pkg installer |
image |
any | plain .zip / .tar.gz archive of the install tree |
Supported platform values: windows-x86_64, windows-arm64,
linux-x86_64, linux-aarch64, macos-x86_64, macos-aarch64.
The macOS formats (macapp / dmg / pkg) support code-signing and
notarization.
Samples
Runnable example apps live under samples/,
each with its own [tool.pyappdist] config:
helloworld— smallest config, no dependencies; a good starting templatepandascli— pandas + numpy (C extensions)datafiles— ships a bundled data file, located viasysconfigmultiprocessingdemo—multiprocessingwithspawnworks unmodifiedpytorchdemo— CUDA PyTorch via a per-index pin (Windows/Linux)matplotlibdemo— TkAgg GUI using the bundled tkinterpygamedemo— pygame-ce GUI (C extensions)pyside6demo— PySide6 (large abi3 wheel, Qt plugins)niceguidemo— NiceGUI + pywebview, per-targetextrasfor backend selection
Status
Beta: Core packaging workflows are ready for real-world use, although configuration details may still change before 1.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyappdist-0.11.0.tar.gz.
File metadata
- Download URL: pyappdist-0.11.0.tar.gz
- Upload date:
- Size: 77.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5164e5b99ba4cf750e9d31e4ed5a36b2d522114b2c5acb730cfa2860cfee5dcb
|
|
| MD5 |
ba28b2d2a906e1969985b4b131a9dd6b
|
|
| BLAKE2b-256 |
fdbdc409fd53465e2b0ef28b9058223cec8abb98ecebd5430f5d19daef57c8e1
|
Provenance
The following attestation bundles were made for pyappdist-0.11.0.tar.gz:
Publisher:
publish.yml on atsuoishimoto/pyappdist
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyappdist-0.11.0.tar.gz -
Subject digest:
5164e5b99ba4cf750e9d31e4ed5a36b2d522114b2c5acb730cfa2860cfee5dcb - Sigstore transparency entry: 2310080288
- Sigstore integration time:
-
Permalink:
atsuoishimoto/pyappdist@3175ff655192f8515563c26bf49efb2073505a22 -
Branch / Tag:
refs/tags/0.11.0 - Owner: https://github.com/atsuoishimoto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3175ff655192f8515563c26bf49efb2073505a22 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file pyappdist-0.11.0-py3-none-any.whl.
File metadata
- Download URL: pyappdist-0.11.0-py3-none-any.whl
- Upload date:
- Size: 98.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28e9d9ded95819817f4373615fc16c64ad9205acf110ef6731fd9ec78c8c100c
|
|
| MD5 |
63fca82109cce08e4c135d28b50d16ac
|
|
| BLAKE2b-256 |
d24074017c5c3cf495026fbb7bf68c50e89bdb634dee5d73cdd4367185251875
|
Provenance
The following attestation bundles were made for pyappdist-0.11.0-py3-none-any.whl:
Publisher:
publish.yml on atsuoishimoto/pyappdist
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyappdist-0.11.0-py3-none-any.whl -
Subject digest:
28e9d9ded95819817f4373615fc16c64ad9205acf110ef6731fd9ec78c8c100c - Sigstore transparency entry: 2310080307
- Sigstore integration time:
-
Permalink:
atsuoishimoto/pyappdist@3175ff655192f8515563c26bf49efb2073505a22 -
Branch / Tag:
refs/tags/0.11.0 - Owner: https://github.com/atsuoishimoto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3175ff655192f8515563c26bf49efb2073505a22 -
Trigger Event:
workflow_dispatch
-
Statement type: