Skip to main content

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

Project description

zuv banner

zuv

zuv packs your entire uv project into a single .py file so you can hand it to a friend, drop it on a server, or attach it to an email and it just runs. The recipient only needs uv installed — no Python install, no pip install -r, no virtualenv setup, no folder structure to preserve.

uv run my-app.py

One file. One command. Same behaviour on any machine.

Why

Shipping a Python project usually means a zip, a README about setting up a venv, a requirements.txt, and a prayer that the recipient has the right Python version. zuv collapses all of that into one .py file that:

  • Carries the whole project inside it (your src/, pyproject.toml, uv.lock, configs, assets — everything you don't .gitignore).
  • On first run, extracts itself into .zuv/<name>_<hash>/ next to the script, lets uv build a .venv inside that folder, then runs your entry point.
  • On every later run, skips the extraction and just executes.
  • Stays tiny (under ~10 KB even for a FastAPI app) because dependencies are installed at runtime by uv from PyPI, not embedded.

Install

uv tool install zuv

Project layout

Any standard uv project works. The most common shape:

my-project/
  pyproject.toml          # [project] dependencies = [...]
  src/
    main.py               # an executable script (with if __name__ == "__main__")

main.py at the project root also works.

Build

From inside the project:

zuv build
# -> ./dist/<project-name>.py

Or point at a project explicitly:

zuv build ./my-project -o ./dist/my-app.py -e src/main.py

zuv build wipes ./dist/ first and writes a fresh single-file script. The entry point is resolved in this order:

  1. --entry/-e flag
  2. [tool.zuv].entry in pyproject.toml
  3. src/main.py if it exists, otherwise main.py

Run

uv run dist/my-app.py

First run: uv extracts the bundle, creates dist/.zuv/<name>_<hash>/.venv, installs deps, runs your entry. Subsequent runs go straight to executing.

Try the included examples

zuv build examples/bigtest -o dist/bigtest.py
uv run dist/bigtest.py

zuv build examples/fastapi -o dist/fastapi.py
uv run dist/fastapi.py

Sibling overrides

The bundle's entry script runs with the extracted project folder as its CWD, so anything you would normally find next to your code (config files, frontend bundles, .env files) still works the same way.

Inspect a built file

zuv inspect dist/my-app.py

Prints the entry, build hash, PEP 723 metadata, and the embedded loader. The base85 payload itself is elided so the output stays useful for LLMs and code review.

A small caveat

Don't name your project the same as one of its dependencies. For example, a project named fastapi that depends on fastapi will confuse uv during install. Rename it to fastapi-example (or similar) and you're fine.

How it works

The output .py has four parts:

  1. A #!/usr/bin/env -S uv run --script shebang and a PEP 723 metadata block declaring requires-python (no deps — uv reads those from the embedded pyproject.toml after extraction).
  2. _ZUV_ENTRY and _ZUV_BUILD_ID module globals.
  3. _ZUV_PAYLOAD — base85 of a tar.gz of your project tree.
  4. A ~30-line loader that decodes the payload, extracts it into .zuv/<stem>_<hash>/, and execs uv run --project <extracted> <entry>.

Dependencies aren't bundled inside the .py. uv installs them into the extracted project's local .venv on first run, so binary wheels work natively and the bundle stays small.

Layout

src/
  pyproject.toml
  zuv/
    cli.py                 # zuv CLI (build, inspect)
    builder.py             # tarball + base85 + emit .py
    inspector.py           # zuv inspect
    _loader_template.py    # runtime loader embedded in every output
    constants.py
examples/
  bigtest/                 # rich + pydantic smoke test
  fastapi/                 # FastAPI + uvicorn web app

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.0.2.tar.gz (7.0 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.0.2-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for zuv-0.0.2.tar.gz
Algorithm Hash digest
SHA256 e901733b6ecc6f177b8ec7a8056d879d2523df9a0e665a5c1c65776f036d0784
MD5 50d0780f56c7ce3de292e858855367ea
BLAKE2b-256 2cdf32e75335cfe738d0f915ea54ecb6732fbd1b2ae6160b1c15d6c13aa072b4

See more details on using hashes here.

Provenance

The following attestation bundles were made for zuv-0.0.2.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.0.2-py3-none-any.whl.

File metadata

  • Download URL: zuv-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 9.5 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.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 7a859fd40e362f581f1df94550ccd595f7fbbc8ab1e5a16b8a1b34e75b94381a
MD5 2b45fd70ec57cd502b5df450ed34d419
BLAKE2b-256 7e89e5f4d6282ae7367ee736ebe559e5f19c43c7cadcdbb4a63f9a49e4d1c124

See more details on using hashes here.

Provenance

The following attestation bundles were made for zuv-0.0.2-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