TL;DR: uv is an extremely fast Python package manager, written in Rust.
We first released uv in February as
a drop-in replacement for common pip
workflows.
Today, we're announcing a series of features that extend uv
beyond a pip
alternative, and into an end-to-end solution for managing
Python projects,
command-line tools,
single-file scripts, and even
Python itself.
It's Cargo, for Python: a unified interface that's fast, reliable, and easy to use.
At Astral, we build high-performance developer tools for the Python ecosystem. We're best known for Ruff, an extremely fast Python linter and formatter.
In February, we released uv, an
extremely fast Python package installer and resolver, initially designed as a drop-in replacement
for common pip
workflows.
Today, we're announcing the largest expansion in uv's capabilities since that initial release:
-
End-to-end project management:
uv run
,uv lock
, anduv sync
. uv can now generate and install from cross-platform lockfiles based on standards-compliant metadata, making it a high-performance, unified alternative to tools like Poetry, PDM, and Rye. -
Tool management:
uv tool install
anduv tool run
(aliased touvx
). uv can now install command-line tools in isolated virtual environments and execute one-off commands without explicit installation (e.g.,uvx ruff check
), making it a high-performance, unified alternative to tools likepipx
. -
Python installation:
uv python install
. uv can now bootstrap and install Python for you, making it a high-performance, unified alternative to tools likepyenv
. -
Script execution: uv can now manage hermetic, single-file Python scripts with inline dependency metadata based on PEP 723. Execute standalone Python scripts with nothing more than
uv run
.
...all powered by an extremely fast, cross-platform dependency resolver.
Resolving the Transformers project with all optional dependencies enabled, without (left) and with (right) caching.
Resolving the Transformers project with all optional dependencies enabled, without (top) and with (bottom) caching.
And all covered in new, comprehensive documentation.
Each of these interfaces represent a significant expansion in scope for uv. But what's unique is how they come together. As a whole, they comprise a new, unified toolchain that takes the complexity out of Python development.
Combined with uv pip
(which we'll continue to maintain and improve as a first-class feature), uv
fits into any Python workflow, from one-off scripting to large, multi-package workspace development.
You can install uv today via our standalone installers, or from PyPI.
Projects #
uv is now capable of managing entire Python projects. If you've used Poetry, PDM, or Rye, you'll find uv's project API to be a familiar alternative.
The project API is built atop Python standards, starting with the use of pyproject.toml
for
declaring project metadata.
For example, you can run uv init && uv add "fastapi>=0.112"
to generate the following:
[project]
name = "hello-world"
version = "0.1.0"
readme = "README.md"
dependencies = ["fastapi>=0.112"]
From there, uv will create a lockfile based on the project's dependencies — here's a snippet:
[[package]]
name = "fastapi"
version = "0.112.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
{ name = "starlette" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2c/09/71a961740a1121d7cc90c99036cc3fbb507bf0c69860d08d4388f842196b/fastapi-0.112.1.tar.gz", hash = "sha256:b2537146f8c23389a7faa8b03d0bd38d4986e6983874557d95eed2acc46448ef", size = 291025 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/b0/0981f9eb5884245ed6678af234f2cbcd40f44570718caddc0360bdb4015d/fastapi-0.112.1-py3-none-any.whl", hash = "sha256:bcbd45817fc2a1cd5da09af66815b84ec0d3d634eb173d1ab468ae3103e183e4", size = 93163 },
]
[[package]]
name = "fastapi-cli"
version = "0.0.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typer" },
{ name = "uvicorn", extra = ["standard"] },
]
sdist = { url = "https://files.pythonhosted.org/packages/c5/f8/1ad5ce32d029aeb9117e9a5a9b3e314a8477525d60c12a9b7730a3c186ec/fastapi_cli-0.0.5.tar.gz", hash = "sha256:d30e1239c6f46fcb95e606f02cdda59a1e2fa778a54b64686b3ff27f6211ff9f", size = 15571 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/24/ea/4b5011012ac925fe2f83b19d0e09cee9d324141ec7bf5e78bb2817f96513/fastapi_cli-0.0.5-py3-none-any.whl", hash = "sha256:e94d847524648c748a5350673546bbf9bcaeb086b33c24f2e82e021436866a46", size = 9489 },
]
uv's lockfile is a snapshot of the project's fully-resolved dependencies at a moment in time, and ensures that the project's environment is consistent across machines.
Critically, the lockfile is cross-platform, in that it can be used to install a given project on any platform, regardless of where it was generated. uv defines a unique solution for every platform, producing a readable and auditable lockfile that defines exactly which packages will be installed.
For example: if you run uv lock
on macOS, uv still generates resolutions for Linux and Windows,
even if the set of required dependencies is slightly different on each platform.
And it does this very quickly. uv can resolve the Jupyter project's dependencies in about half a second without any caching (and in around 20 milliseconds with a warm cache).
The center point of the project API is uv run
, which runs a command in the project's environment
(without requiring explicit activation of a virtual environment).
uv run
is fast enough that every invocation will implicitly re-lock and re-sync the project,
ensuring that your environment is up-to-date without the need for manual intervention.
In other words, uv run
guarantees that your command is executed in a consistent, locked
environment.
Running a FastAPI server via uv run
.
With uv run
, you don't have to think about activating virtual environments, managing dependencies,
or keeping your project up-to-date. It just works.
For more, see the project documentation.
Dependency sources: editables, relative paths, and more #
uv is built on Python standards, like PEP 621. But it also adds features that are essential to local development but not covered by the standards, like relative paths and editable dependencies.
So while a project's dependencies (and publishable metadata) are defined in the standards-compliant
project.dependencies
table, uv allows you to define alternate "sources" for each dependency to use
during development via tool.uv.sources
.
For example, to use a local, editable version of anyio
, you can run uv add --editable ../anyio
to produce the following pyproject.toml
:
[project]
name = "hello-world"
version = "0.1.0"
readme = "README.md"
dependencies = ["anyio"]
[tool.uv.sources]
anyio = { path = "../anyio", editable = true }
When published to PyPI, the above package would declare a dependency on anyio
; but when synced
locally via uv run
, the editable package at ../anyio
would be included in the development
environment instead. While the project's dependencies haven't changed, the sources from which those
dependencies are drawn have.
For more, see the dependency sources documentation.
Workspaces #
Inspired by the Cargo concept of the same name, uv supports workspaces: collections of one or more packages that are managed together.
Think: a FastAPI-based web application, alongside a series of libraries that are versioned and maintained as separate Python packages, all in the same Git repository.
Most large Python projects end up implementing their own version of this concept, with hand-rolled scripts and custom tooling to manage the relationships between packages. uv's workspace API provides a consistent, high-performance, and opinionated solution to the kinds of development challenges that appear at scale.
In a workspace, each package has its own pyproject.toml
, but the workspace shares a single
lockfile, ensuring that the workspace operates with a consistent set of dependencies.
[project]
name = "fastapi"
version = "0.1.0"
readme = "README.md"
dependencies = ["uvicorn"]
[tool.uv.sources]
uvicorn = { workspace = true }
[tool.uv.workspace]
members = ["libraries/*"]
From the workspace root, you can run commands within any given workspace member with (e.g.)
uv run --package fastapi
or uv run --package uvicorn
.
For more, see the workspace documentation.
Tools #
uv is now capable of installing command-line tools (like Ruff)
in dedicated, isolated virtual environments (uv tool install
) – and running one-off commands
without any explicit installation step (uvx
).
If you've used pipx or npx, you'll find uv's tool API to be a familiar alternative.
For example, you can run Darren Burns' posting
TUI with
nothing more than uvx posting
.
Running the posting TUI in a terminal via uvx
.
The tool API enables you to centrally manage the Python applications that live on your system. For
example, you can run uv tool list
to view all installed tools (along with the executables they
expose), or uv tool upgrade --all
to upgrade all installed tools to their latest supported
versions.
uv's tool API is extremely fast. Executing commands via uvx
adds virtually no overhead.
For more, see the tools documentation.
Python bootstrapping #
uv is now capable of installing and managing Python itself, making it entirely self-bootstrapping:
$ curl -LsSf https://astral.sh/uv/install.sh | sh
$ uv python install 3.12
In addition to any explicit uv python
invocations, uv can automatically download Python versions
as needed (on uv run
, uv venv
, etc.), e.g., if you're yet to install Python on a machine, or are
lacking a specific, requested Python version.
For example, here's the entire sequence of commands necessary to run posting
on a fresh Ubuntu
Docker image – there are no hidden steps:
$ apt-get update && apt-get install -y curl
$ curl -LsSf https://astral.sh/uv/install.sh | sh
$ source $HOME/.cargo/env
$ uvx posting
Upon invoking uvx
, uv will automatically install Python 3.12.4, create an ephemeral virtual
environment, install the package, and invoke the posting
CLI. It's really easy.
Single-file scripts #
Finally: uv now includes first-class support for single-file Python scripts with inline dependency metadata, as defined in PEP 723.
For example, consider the following main.py
, which fetches the first ten PEPs from the Python
Enhancement Proposal index:
import requests
from rich.pretty import pprint
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])
This script depends on requests
and rich
, but doesn't include any explicit dependency metadata.
Historically, you'd need to manage these dependencies separately from the script, e.g., in a
dedicated requirements.txt
file.
Now, you can use uv add
to automatically embed the dependency declarations within the script
itself:
$ uv add --script main.py "requests<3" "rich"
$ cat main.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
import requests
from rich.pretty import pprint
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])
From there, uv run main.py
will execute the script in an isolated, ephemeral virtual environment
with all of its dependencies installed.
Running a single-file script with embedded dependencies via uv run
.
You can also pull arbitrary dependencies into a uv run
invocation via with the --with
flag, as
in: uv run --with "requests<3" --with rich main.py
.
Combined with uv's built-in Python bootstrapping, you can now run hermetic, redistributable, single-file Python scripts with nothing more than uv (and without worrying about virtual environments, dependency management, or Python versions).
For more, see the scripts documentation.
Built for scale #
At Astral, one of our guiding principles is: "Design for everyone, build for scale."
We want to build tools that are accessible to beginners, but that can scale up to the needs of the largest Python projects.
With uv, we think we've achieved both.
If you're using uv internally and are interested in collaborating with us as we take on our next set of Python packaging challenges, get in touch. We'd love to build with you.