# Getting started with uv


[uv](https://pydevtools.com/handbook/reference/uv.md) handles Python installation, project setup, dependency management, and script execution in a single tool. This tutorial walks through each of those capabilities so you can start using uv for any Python project.

> [!TIP]
> Just want to run a single script, not build a project? Start with [Run your first Python script](https://pydevtools.com/handbook/tutorial/how-to-run-your-first-python-script.md). For the full linting, type-checking, and testing stack, follow [Set up a complete Python project](https://pydevtools.com/handbook/tutorial/set-up-a-complete-python-project.md) after this.

## Prerequisites

You need a terminal (Terminal on macOS, PowerShell on Windows, or any Linux shell). No prior Python installation is required.

[Git](https://git-scm.com/downloads) is optional. If Git is on your `PATH`, `uv init` initializes a repository in the new project. uv does not install Git itself; install it separately if you want version control. The tutorial steps work either way.

## Installing uv

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

Alternatively, install with Homebrew:

```bash
brew install uv
```

```powershell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```

Verify the installation:

```console
$ uv --version
uv 0.11.x
```

If you see `command not found: uv`, the installer printed a hint about updating your shell `PATH`. Open a new terminal window (or restart the current shell) so the updated `PATH` takes effect.

For platform-specific details, see [How to install uv](https://pydevtools.com/handbook/how-to/how-to-install-uv.md).

## Creating a project

### Initialize a new project

```console
$ uv init my-project
Initialized project `my-project` at `/path/to/my-project`
$ cd my-project
```

### Examine the generated files

`uv init` creates a ready-to-use project structure:

{{< /filetree/folder >}}
{{< /filetree/container >}}

Open [pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md) to see your project metadata:

```toml
[project]
name = "my-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []
```

The `.python-version` file records which Python version the project uses. uv reads this file automatically and downloads that interpreter if it isn't already installed. See [What is a Python version file?](https://pydevtools.com/handbook/explanation/what-is-a-python-version-file.md) for more on how this works.

### Run the starter script

```console
$ uv run main.py
Using CPython 3.13.5
Creating virtual environment at: .venv
Hello from my-project!
```

On first invocation, `uv run` creates a [virtual environment](https://pydevtools.com/handbook/explanation/what-is-a-virtual-environment.md) in `.venv/`, installs the correct Python version, syncs any dependencies, and then runs your script. Subsequent invocations skip the setup and just run your script.

Notice the new `.venv/` directory in the project. That is where the project's Python interpreter and installed packages live. uv manages it for you, so there is no need to activate it manually.

> [!TIP]
> `uv init` creates an application project by default. For libraries, CLI tools, or other structures, see [Understanding uv init project types](https://pydevtools.com/handbook/explanation/understanding-uv-init-project-types.md).

## Adding dependencies

### Add a package

If you see `error: No `pyproject.toml` found in current directory or any parent directory`, you are running `uv add` outside the project. `cd` into the `my-project/` directory you created with `uv init`, then retry.

```console
$ uv add requests
Resolved 6 packages in 101ms
Installed 5 packages in 9ms
 + certifi==2025.1.31
 + charset-normalizer==3.4.1
 + idna==3.10
 + requests==2.32.3
 + urllib3==2.3.0
```

This command does three things: updates `dependencies` in `pyproject.toml`, resolves compatible versions and writes a [lockfile](https://pydevtools.com/handbook/explanation/what-is-a-lock-file.md) (`uv.lock`), and installs the package into the project's virtual environment.

Notice the new `uv.lock` file in the project root. It pins the exact version of every direct and transitive dependency, so anyone running `uv sync` later gets the identical environment. Commit it alongside `pyproject.toml`.

Your `pyproject.toml` now includes:

```toml
dependencies = [
    "requests>=2.32.3",
]
```

### Use the package in your code

Replace the contents of `main.py` with:

```python
import requests

response = requests.get("https://httpbin.org/json")
data = response.json()
print(data["slideshow"]["title"])  # prints the title from the response
```

Run it:

```console
$ uv run main.py
Sample Slide Show
```

### Add a development dependency

Development tools like linters and test runners belong in a separate dependency group so they don't ship with your project:

```console
$ uv add --dev ruff
Resolved 7 packages in 83ms
Installed 1 package in 3ms
 + ruff==0.15.21
```

This adds `ruff` under a development dependency group in `pyproject.toml`. Run it with:

```console
$ uv run ruff check .
All checks passed!
```

### Remove a package

```console
$ uv remove requests
Resolved 2 packages in 4ms
Uninstalled 5 packages in 6ms
 - certifi==2025.1.31
 - charset-normalizer==3.4.1
 - idna==3.10
 - requests==2.32.3
 - urllib3==2.3.0
```

This updates `pyproject.toml`, regenerates `uv.lock`, and uninstalls the package from the virtual environment.

## Running code and tools

`uv run` is the primary way to execute anything inside your project's environment. It ensures dependencies are synced before every invocation.

Run a Python script:

```console
$ uv run main.py
Hello from my-project!
```

Run a module. `uv run -m http.server 8000` starts a local web server and blocks the terminal. Press `Ctrl+C` to stop it:

```console
$ uv run -m http.server 8000
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
```

Run a tool installed as a dependency:

```console
$ uv run ruff format .
1 file left unchanged
```

Notice that `uv run ruff format` reports every file it touches, even when nothing changes. A `1 file left unchanged` line confirms ruff ran and found nothing to reformat.

To run a CLI tool that isn't part of your project (a one-off invocation), use `uvx`:

```console
$ uvx cowsay -t "hello uv"
  ________
| hello uv |
  ========
        \
         \
           ^__^
           (oo)\_______
           (__)\       )\/\
               ||----w |
               ||     ||
```

If you see `usage: Cowsay [-h] ... error: the following arguments are required: -t/--text`, you omitted the `-t` flag. `uvx cowsay` requires `-t` before the message text.

For a deeper comparison, see [When to use uv run vs uvx](https://pydevtools.com/handbook/explanation/when-to-use-uv-run-vs-uvx.md).

## Managing Python versions

uv downloads and manages Python interpreters directly. No separate tool like pyenv is needed.

### List available versions

```console
$ uv python list
cpython-3.14.0-macos-aarch64-none                 <download available>
cpython-3.13.5-macos-aarch64-none                 /Users/you/.local/share/uv/python/cpython-3.13.5-macos-aarch64-none/bin/python3.13
cpython-3.13.5+freethreaded-macos-aarch64-none    <download available>
cpython-3.12.7-macos-aarch64-none                 <download available>
...
```

The output lists every Python build uv can manage. Versions already on disk show a path; versions marked `<download available>` will be fetched on first use. Your output will differ based on platform, architecture, and which versions you have already installed.

### Install a specific version

```console
$ uv python install 3.12
Installed Python 3.12.7 in 80ms
 + cpython-3.12.7-macos-aarch64-none (python3.12)
```

If the version is already installed, uv prints `Python 3.12 is already installed` instead and exits without changes.

### Pin a version to your project

The pinned version must satisfy the `requires-python` constraint in `pyproject.toml`. If `requires-python = ">=3.13"`, pinning to `3.12` fails with `error: The requested Python version `3.12` is incompatible with the project `requires-python` value of `>=3.13`.` Loosen `requires-python` first, or pin a compatible version.

```console
$ uv python pin 3.12
Pinned `.python-version` to `3.12`
```

This writes `3.12` to the `.python-version` file. The next time you run `uv run`, uv uses that version (installing it first if necessary). Open `.python-version` to confirm the change; it contains a single line with the pinned version.

### Test against a different version

You can run a single command with a different Python version without changing the project pin. If uv does not have that version on disk, it downloads it first:

```console
$ uv run --python 3.11 main.py
Installed Python 3.11.13 in 3.21s
 + cpython-3.11.13-macos-aarch64-none (python3.11)
Hello from my-project!
```

Subsequent invocations with `--python 3.11` skip the download and run immediately. If you see `error: The requested Python version 3.11 is incompatible with the project's Python requirement`, the `requires-python` field in `pyproject.toml` excludes that version. Either loosen `requires-python` or pick a version within the allowed range.

## What to explore next

You now know how to install uv, scaffold a project, manage dependencies, run code, and switch Python versions. Each of these topics goes deeper:

- [Complete guide to uv](https://pydevtools.com/handbook/explanation/uv-complete-guide.md) covers workspaces, lockfile strategies, and advanced configuration
- [Create your first Python project](https://pydevtools.com/handbook/tutorial/create-your-first-python-project.md) builds a text analysis tool from scratch using uv
- [Set up Ruff for formatting and checking your code](https://pydevtools.com/handbook/tutorial/set-up-ruff-for-formatting-and-checking-your-code.md) adds linting and formatting to a uv project; see the [Ruff reference](https://pydevtools.com/handbook/reference/ruff.md) for its features and configuration
- [Setting up testing with pytest and uv](https://pydevtools.com/handbook/tutorial/setting-up-testing-with-pytest-and-uv.md) sets up a test suite with fixtures and coverage
- [How to translate pip commands to uv](https://pydevtools.com/handbook/how-to/how-to-translate-pip-commands-to-uv.md) maps familiar pip workflows to uv equivalents
- [uv reference page](https://pydevtools.com/handbook/reference/uv.md) for a concise overview of features, pros, and cons
- [Modern Python Tooling Checklist](https://pydevtools.com/tooling-checklist.md) collects these defaults on one page for when you standardize a project or team template
