Skip to main content

bundleup

PyPI Python CI License: MIT

Bundle up your Python app with everything it needs. One command turns a locked project into a single .pyz that runs on plain Python: no install step, no network, native extensions included. And it tells you before you ship what won't survive the trip.

Dead simple to use, but also powerful and fast: esbuild's experience, for Python.

Status: alpha (0.2.0). Builds for this machine or another platform (Linux, macOS, Windows; x86_64 and arm64), passes the gauntlet on all four in CI, and the own test suites of click, packaging, markupsafe and itsdangerous give the same results from a bundle as from a venv (nightly). Try it without installing via uvx bundleup build, or install with uv tool install bundleup / pip install bundleup.

Usage

bundleup build                  # the project in this directory -> dist/<name>.pyz
bundleup build path/to/script.py  # a PEP 723 script with inline dependencies
bundleup build --python 3.9     # build with Python 3.9 (pure Python: runs on 3.9 and newer)
bundleup build --python 3.11 --python-platform linux   # build on a Mac for Linux x86_64
bundleup build --python-platform linux,macos,windows   # one file for all three
bundleup build --external torch # leave a heavy host package out of the bundle
bundleup build --json           # the result as JSON on stdout, for scripts and agents
bundleup check                  # what won't survive bundling, and each package's size
bundleup check --python-platform linux,windows   # also check Windows wheels
bundleup check --matrix         # which OS, CPU and Python versions the lock's wheels cover
bundleup build --smoke          # then run it once: fresh home folder, no network
bundleup build --max-size 30MB # fail, writing nothing, if the bundle is bigger
bundleup build --format dir     # a plain directory, for apps that load packages from one
bundleup build --format zip     # a flat .zip of code, packages and bytecode (e.g. serverless)
bundleup build --format py      # one plain-text .py file (for small tools: gists, uploads)
bundleup build --split 100MB    # no file over 100 MB: a small .pyz and a folder of parts
bundleup build --format exe     # experimental: an executable with its own Python, unsigned
bundleup verify dist/app.pyz    # bundle and unpacked copy vs its manifest (corruption; not a signature)
bundleup cache clean            # remove unpacked bundles not used for 30 days
python dist/<name>.pyz          # run it: no install, no network

bundleup reads what you have: pyproject.toml with uv.lock or pylock.toml, a script's # /// script block, or, without a lock, a pyproject.toml, setup.py or requirements.txt (resolved at build time, with a warning; recipes). It brings its own uv for the build; the bundle needs only Python. On first run it unpacks to a cache (~/Library/Caches/bundleup, ~/.cache/bundleup, or a temp directory if those aren't writable), so later runs start as fast as an installed virtualenv; unpacking a new version removes older copies of the same bundle that went unused for 30 days. A pure-Python bundle runs on every Python version its lock allows (Python 3.10+), and on any OS when the lock picks the same packages everywhere (on any OS); one with compiled code runs on the version and platform it was built for. A bundle only sees its own packages and the standard library; pass --external <pkg> (or set BUNDLEUP_INHERIT_PATH=1 at runtime) to also let it use packages installed on the machine (they come after the bundle's, but a machine package can still win over a bundled namespace package of the same name: a Python rule, PEP 420). If it's started with the wrong Python, it runs itself again with a matching one if one is installed; otherwise, and on the wrong platform, it says so in one sentence. Programs it starts with its own Python see its packages; any other Python it starts doesn't. bundleup never writes into your project.

Every build checks the code first: a file of your project that doesn't compile on the target Python stops the build, and anything that may not work in a bundle (a dependency's file that needs a newer Python, data files a package expects under sys.prefix) is a warning. bundleup check runs the same checks without writing a bundle and shows how big each package is; --strict makes warnings fail too.

Recipes for Claude Skills, AWS Lambda, host applications, CI and HPC: docs/recipes.md.

Why

Python has three ways to hand a program to someone else, and the middle one is missing:

Ship as The other machine needs Problem
Standalone executable Nothing Per-OS builds, code signing, antivirus (bundleup can wrap a bundle as one, unsigned: --format exe)
Source + pip install / uv sync Python, an installer, network The install step is where things break
One file that runs on the user's Python Python No easy, reliable tool. That's bundleup

pex, shiv and zipapps can do parts of this. bundleup's bet is a much better experience on top of the same correct approach: no flags to get it fast, plain-language errors, and a pre-ship check. Measured against pex's fastest configuration: builds 1.1-2.4× faster, warm starts as fast as an installed venv (pex adds 45-80 ms), first runs 2.3-15× faster (findings). See docs/vision.md.

Where to start reading

Gauntlet

uv run gauntlet/check_native.py                         # control group: projects run normally
uv run gauntlet/run_bundlers.py --conditions --out run  # existing bundlers, incl. hostile conditions
uv run gauntlet/report.py gauntlet/results/run.json > gauntlet/results/run.md

License

MIT

Metadata

Release files for bundleup 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bundleup 0.2.0
File Size Uploaded
bundleup-0.2.0.tar.gz 127.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bundleup 0.2.0
File Interpreter ABI Platform
bundleup-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 268.6 kB

Release files / bundleup-0.2.0.tar.gz

Download URL bundleup-0.2.0.tar.gz
Size 127.0 kB
Tags Source
SHA-256 checksum
How to use checksums
ea47199d187215809cf2a8b8637f0d3a98ce084436c20bf825959ae4b5aff7be
BLAKE2b-256 checksum
How to use checksums
2f49d38255e9a19ccbdba1096bcde9c8bfe90d59f21d0fff22086c08a0296ae4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / bundleup-0.2.0-py3-none-any.whl

Download URL bundleup-0.2.0-py3-none-any.whl
Size 141.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f4fc9ca27eabf54446b9fe1eea92d4fb282f4617e0f4da147b769032d2ada07f
BLAKE2b-256 checksum
How to use checksums
3384aacfa131f502b410ce19846171459cbd9f7f846559d51d246bd4cf8e3c3a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page