Philpax icon

Philpax

Programming · Using uv to ship a Python application

Recently, as part of some work I did on Wayfarer Labs' OWL Control, I had to figure out how to wire up Astral's uv to consistently ship a self-enclosed Python + environment to run Python code on a user's machine. Here's some documentation on that.

Before I continue, there are two things I should say:

  • please don't use Python for this if you can, at all, avoid it. In fact, just don't use Python.
  • check this issue first; it is possible that by the time you've read this, uv has already shipped a first-class solution for this

Anyway, you must first get uv, your codebase, its pyproject.toml, and its uv.lock to the user's machine. An installer will do for this.

Once there, invoke uv sync with these environment variables:

Bourne Again Shell (bash)
# Do not attempt to update the dependencies UV_FROZEN='1' # Always copy deps do not hardlink UV_LINK_MODE='copy' # Do not let the user's configuration interfere with our uv UV_NO_CONFIG='1' # Mark all dependencies as non-editable UV_NO_EDITABLE='1' # Ensure we always use our managed Python UV_MANAGED_PYTHON='1' # Update all directories UV_CACHE_DIR='./.uv/cache' UV_PYTHON_INSTALL_DIR='./.uv/python_install' UV_PYTHON_BIN_DIR='./.uv/python_bin' UV_TOOL_DIR='./.uv/tool' UV_TOOL_BIN_DIR='./.uv/tool_bin'

This will force uv to work locally within a .uv folder and not pollute / interfere with any existing Python / uv installations the user may have.

Once uv sync is done, all dependencies have been installed, which means you can then run your script with uv run (again, with the same environment variables).

I chose to keep these steps separate so that some form of progress could be shown to the user, but you can also just skip the uv sync step entirely if you're OK with uv run taking some arbitrary time before it runs your code.

Anyway, I beseech you: please don't ship Python to users' machines. It's no good at it.