# How to change the Python version of a uv project

To change the Python version of a [uv](https://pydevtools.com/handbook/reference/uv.md) project, run `uv python pin 3.12` to update `.python-version`, then `uv sync` to rebuild the virtual environment against the new interpreter. If you also want to change which versions the project *supports*, edit `requires-python` in `pyproject.toml` and run `uv lock && uv sync`.

A uv project tracks Python versions in two places: the [.python-version file](https://pydevtools.com/handbook/explanation/what-is-a-python-version-file.md) pins the development interpreter (e.g., `3.12`), and `requires-python` in [pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md) declares which versions the project supports (e.g., `>=3.11`). The pinned version must satisfy the `requires-python` constraint, or uv will report an [incompatibility error](https://pydevtools.com/handbook/how-to/how-to-fix-python-version-incompatibility-errors-in-uv.md).

## See what Python versions are available

Before you switch or upgrade, check what uv can install:

```console
$ uv python list
```

This lists the newest patch release of each CPython and PyPy version uv can download, along with the interpreters already on your system. Add `--all-versions` to see every patch release of each. Pick a version from this list.

## Pin a different development interpreter

```console
$ uv python pin 3.12
```

This writes `3.12` to `.python-version`, which uv reads on every command.

Then re-sync so the virtual environment picks up the new interpreter:

```console
$ uv sync
```

If `3.12` isn't already installed, uv downloads it during the sync. `uv sync` recreates `.venv` against the pinned version when the interpreter changes, so you usually don't need to delete the virtual environment by hand. If a sync fails partway through or the venv ends up in an inconsistent state, force a clean rebuild:

```console
$ uv venv --python 3.12 --clear
$ uv sync
```

## Upgrade a patch release or switch the minor version

uv handles patch upgrades and minor-version changes differently, and mixing them up is the usual source of confusion about "changing" a project's Python.

`uv python pin 3.12` records a loose request for any `3.12.x`. A sync then uses the newest `3.12.x` already installed on your machine (for example, `3.12.13`); it does not download a newer patch on its own. Because the pin isn't tied to one patch, you don't re-pin to move within `3.12`.

To fetch a newer patch, upgrade the interpreter, then sync:

```console
$ uv python upgrade 3.12
$ uv sync
```

`uv python upgrade` downloads the latest patch (`3.12.13` to `3.12.14`); the loose `3.12` pin accepts it on the next sync with no re-pin, because a patch bump doesn't change the language version your dependencies resolve against. See [how to keep Python up to date with `uv python upgrade`](https://pydevtools.com/handbook/how-to/how-to-keep-python-up-to-date-with-uv-python-upgrade.md) for the full workflow.

Changing the minor version is always explicit. Re-pin with `uv python pin 3.13`, then run `uv sync`. uv never crosses minor versions on its own, because a minor bump can change which dependency versions resolve.

To pin one exact patch and stop tracking the latest, pin the full version:

```console
$ uv python pin 3.12.8
```

## Change the supported Python versions

Edit `requires-python` in `pyproject.toml`:

```toml
[project]
requires-python = ">=3.11"
```

Then regenerate the lockfile and re-sync:

```console
$ uv lock
$ uv sync
```

`uv lock` re-resolves all dependencies against the new constraint. `uv sync` updates the virtual environment to match.

> [!TIP]
> Raising the minimum Python version can unlock newer dependencies and drop compatibility workarounds, but it excludes users on older interpreters.

## Try another interpreter without editing the pin

`--python` overrides the `.python-version` pin for one invocation, which is how you check whether the project still works on an older or newer interpreter before committing to it. The flag works on most uv commands, not just `uv run`:

```console
$ uv run --python 3.10 python -c "import sys; print(sys.version)"
$ uv sync --python 3.13
$ uv add --python 3.13 httpx
```

The pin file is untouched, but `.venv` is not. Each of these rebuilds the project environment on the requested version and leaves it there:

```console
$ .venv/bin/python -V
Python 3.10.20
```

Editors and shells pointed at `.venv` follow it down until the next project command without `--python` rebuilds it, whether that's `uv run`, `uv sync`, or `uv add`. Run `uv sync` when the check is done rather than waiting to be surprised. To run your whole test suite against several interpreters this way, see [how to test against multiple Python versions using uv](https://pydevtools.com/handbook/how-to/how-to-test-against-multiple-python-versions-using-uv.md).

## Keep CI on the same Python version

CI systems and Docker images should honor the same `.python-version` pin so local and remote builds agree. In GitHub Actions, install uv with [`astral-sh/setup-uv`](https://github.com/astral-sh/setup-uv) and let `uv sync` install the pinned interpreter on demand (or call `uv python install`); in Docker, copy `.python-version` into the image before `uv sync` so the build matches local development. See [how to install Python with uv](https://pydevtools.com/handbook/how-to/how-to-install-python-with-uv.md) for interpreter management details.

## Frequently asked questions

### Do I need to delete `.venv` when switching Python versions?

No. `uv sync` rebuilds the virtual environment against the pinned interpreter. Only use `uv venv --clear` if a sync fails partway through or the venv gets into an inconsistent state.

### Does changing the Python version break my lockfile?

Usually not. `uv.lock` is resolved against `requires-python`, not the pinned development version, so changing `.python-version` inside that range leaves the lockfile valid. Editing `requires-python` itself triggers a re-resolve on the next `uv lock`.

### What if the Python version isn't installed on my machine?

uv downloads managed Python builds on demand: running `uv sync` or `uv run` with a version that isn't installed will fetch it automatically the first time it's needed. To install a specific version up front, run `uv python install 3.13`.

### How do I downgrade the Python version with uv?

Pin the older minor version with `uv python pin 3.10`, then run `uv sync`. The pin must still satisfy `requires-python` in `pyproject.toml`; if the downgrade falls below your current minimum, lower the floor there (for example to `>=3.10`) and run `uv lock` first.

## Related

- [uv: A Complete Guide](https://pydevtools.com/handbook/explanation/uv-complete-guide.md) covers what uv does, how fast it is, the core workflows, and recent releases.
- [What is a .python-version file?](https://pydevtools.com/handbook/explanation/what-is-a-python-version-file.md)
- [How to install Python with uv](https://pydevtools.com/handbook/how-to/how-to-install-python-with-uv.md)
- [How to fix Python version incompatibility errors in uv](https://pydevtools.com/handbook/how-to/how-to-fix-python-version-incompatibility-errors-in-uv.md)
- [How do pyenv and uv compare for Python interpreter management?](https://pydevtools.com/handbook/explanation/how-do-pyenv-and-uv-compare-for-python-interpreter-management.md)
