Skip to main content

Bundle any uv project into a single runnable .py file.

Project description

zuv banner

zuv

Pack your uv project into one .py file. Hand it over, drop it on a server, email it — uv run app.zuv.py and it works.

Why

  • One file, no setup. No zip + README + requirements.txt + venv dance. Recipient runs one command.
  • Cross-platform, cross-Python. Same file works on Windows / Linux / macOS and any Python minor version (bytecode is built on the target).
  • Tiny. ~10 KB even for a FastAPI app — deps install at first run, they're not embedded (unless you ask with --deps).
  • Just uv. No bespoke runtime, no PyInstaller-style freezing. The bundle is a PEP 723 script; uv run is the entrypoint.

Install

uv tool install zuv

Quick start

zuv build                       # -> ./dist/<name>.zuv.py
uv run dist/<name>.zuv.py       # run it

dist/ is wiped on every build. Bundles ship .py sources (no bytecode), so the same file works on any OS and any Python minor version. First run extracts into dist/.zuv/<name>_<hash>/, installs deps into a local .venv, runs your entry. Later runs skip extraction.

Commands

zuv build [project] [flags]

arg / flag what it does
project Path to the uv project (containing pyproject.toml). Default: current directory.
-o, --output Output file path. Default: ./dist/<project-name>.zuv.py (or .zuv.zip with --zip).
-e, --entry Entry script relative to project root. Default: [tool.zuv].entry, then src/main.py, then main.py.
--zip Wrap the .zuv.py in a .zuv.zip with run.bat (Windows) + run.sh (Unix/macOS) launchers. The launchers install uv from https://astral.sh/uv if missing, then run the bundle. For recipients with neither uv nor Python.
--deps [LIST] Embed wheels for the locked deps so the bundle runs offline. Bare = current OS only. all = every supported platform. Comma list = pick from windows, linux, linux-arm, macos, macos-arm. Wheels are tied to the Python minor you build with. Note: wheels can't carry OS libs (libGL, libpq, …); on bare Linux, apt install what ImportError names. Prefer -headless variants.
--no-compile Tell the loader to skip the first-run .py.pyc compile. Cache stays as plain .py; per-import startup is slower.
--update-repo REPO Make the bundle self-update from a GitHub or GitLab repo's Releases. Accepts a URL (https://github.com/user/repo, https://gitlab.com/user/repo) or shorthand (user/repo → GitHub, gitlab:user/repo → GitLab). On every startup it asks the Releases API for the named asset and the release's tag_name. If the bundled [project] version from pyproject and the remote tag both parse as dotted-int versions, the loader skips when local ≥ remote (no prompt for older or equal). Otherwise (e.g. rolling latest tag) it falls back to comparing the asset's id; prompts install latest version? [Y/n] when something changed. Declined versions are remembered. Non-TTY runs skip silently. Private repos: set $GH_TOKEN (GitHub) or $GITLAB_TOKEN (GitLab). ZUV_NO_UPDATE=1 disables.
--update-tag TAG Release tag to fetch the asset from. Default: latest (special value: hits the provider's /releases/latest endpoint). Use a fixed tag to pin recipients to one release.
--update-file FILENAME Asset filename inside the release. Default: <output-stem>.zuv.py. Lets one release host several bundles (e.g. fastapi.zuv.py, dashboard.zuv.py).

zuv run <file> [-- script-args...]

Thin wrapper around uv run <file>. Identical to running the bundle directly with uv.

zuv inspect <file>

Print the entry, build hash, sha256, PEP 723 metadata, and a summary of the embedded loader bytecode. Payload is elided.

zuv clean [target]

Remove every .zuv/ extraction cache under target (default: cwd). target can be a directory or a built .zuv.py (its parent is used).

Runtime env vars

var default purpose
ZUV_CACHE_DIR next to script Override where the bundle extracts.
ZUV_MAX_EXTRACT_BYTES 2 GiB Decompression-bomb cap.
ZUV_NO_UPDATE (unset) If set, disables the --update-repo self-update check at startup.
GH_TOKEN / GITHUB_TOKEN (unset) Sent as Authorization: Bearer … on GitHub update checks. Required for private GitHub repos.
GITLAB_TOKEN (unset) Sent as PRIVATE-TOKEN: … on GitLab update checks. Required for private GitLab repos.

If the script's directory isn't writable, the loader falls back to $XDG_CACHE_HOME/zuv / %LOCALAPPDATA%\zuv / ~/.cache/zuv.

How it works (1 paragraph)

The output is a PEP 723 script: shebang, metadata, a base85-encoded tar.xz of your project (_ZUV_PAYLOAD), and a tiny loader (_ZUV_LOADER) that verifies the sha256, extracts into .zuv/<stem>_<hash>/, and runs uv run --project <extracted> <entry>. Deps install at first run, so the bundle stays small.

Caveat

Don't name your project the same as one of its dependencies (fastapi depending on fastapi confuses uv). Use fastapi-example or similar.

Examples

zuv build examples/bigtest && uv run dist/bigtest.zuv.py
zuv build examples/fastapi && uv run dist/fastapi.zuv.py

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

zuv-0.3.0.tar.gz (19.2 kB view details)

Uploaded Source

Built Distribution

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

zuv-0.3.0-py3-none-any.whl (24.8 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for zuv-0.3.0.tar.gz
Algorithm Hash digest
SHA256 3472cd997b15b30d268f76880084e4bc92753d84599f952df0ae986a6f4aeb9a
MD5 1c0024918f250632d4f7d992c5e8b5a0
BLAKE2b-256 a187c69f80f6cb2bb9a8f96d9e87019956aca6169db9e8b4ab352c66f7ce1630

See more details on using hashes here.

Provenance

The following attestation bundles were made for zuv-0.3.0.tar.gz:

Publisher: pypi.yml on HamzaYslmn/zuv

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

File details

Details for the file zuv-0.3.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for zuv-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 32bbb17767d6724a582c2bd8684b2c6b4586067aab2e12779fa759bd0c94436c
MD5 45d02fd87f836855fa4559b1a609c63d
BLAKE2b-256 e869af4bfa842cb83c740e72fcec8f7195a5e0d35755cfde9db5fc5e893345e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for zuv-0.3.0-py3-none-any.whl:

Publisher: pypi.yml on HamzaYslmn/zuv

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