Skip to main content

bundleup

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.1.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 --python-platform macos --python-platform windows   # one file for all three
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 --also-platform windows   # which packages lack a wheel for another OS
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 check --audit          # ask PyPI: vulnerable, yanked or brand-new dependencies?
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 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; set BUNDLEUP_INHERIT_PATH=1 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.1.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.1.0
File Size Uploaded
bundleup-0.1.0.tar.gz 115.8 kB Details

Built distribution (wheel)

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

Total release size: 247.3 kB

Release files / bundleup-0.1.0.tar.gz

Download URL bundleup-0.1.0.tar.gz
Size 115.8 kB
Tags Source
SHA-256 checksum
How to use checksums
ba9cc6fe6c4ab0e537538ca7ab5625154c60535d5e2e90e48a6a9ee3095fc441
BLAKE2b-256 checksum
How to use checksums
bd24d634aa249d67df76871fde1a4954a6685beb61a0cc5d13c8abd3c696e68f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","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.1.0-py3-none-any.whl

Download URL bundleup-0.1.0-py3-none-any.whl
Size 131.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c3519080b491c6a64cdc10792036f00ba9bfd067b6ae3ab16950ec4cf8959338
BLAKE2b-256 checksum
How to use checksums
ea10e6eb22f8fe686ed5e8d242bbe4bdf31a9d91c952980afac1af831213ef79
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","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.1.0 This release

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