# How to use free-threaded Python in a uv project


Python 3.14 is the first release where the free-threaded build ships as officially supported, after [PEP 703: Making the Global Interpreter Lock Optional in CPython](https://pydevtools.com/handbook/explanation/what-is-pep-703.md) introduced it experimentally in 3.13 by removing the [GIL](https://pydevtools.com/handbook/explanation/what-is-the-gil.md) and [PEP 779: Criteria for supported status for free-threaded Python](https://pydevtools.com/handbook/explanation/what-is-pep-779.md) moved it to phase II in 3.14. This how-to switches an existing [uv](https://pydevtools.com/handbook/reference/uv.md) project to that build, with the dependency-compatibility step that will bite you if you skip it.

If you want to see the performance benefit first on a throwaway project, start with [Try Free-Threaded Python with uv](https://pydevtools.com/handbook/tutorial/try-free-threaded-python-with-uv.md).

## Install the free-threaded interpreter

```bash
uv python install 3.14t
```

The `t` suffix requests the free-threaded build. uv keeps standard and free-threaded interpreters side by side, so this does not disturb any existing 3.14 install.

## Adjust `requires-python` first

`3.14t` is a build variant of Python 3.14, not a separate language version. It satisfies constraints like `>=3.14` but not `>=3.15`. Before pinning, confirm that `requires-python` in `pyproject.toml` admits 3.14:

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

If `requires-python` is tighter than the language version of the free-threaded build, pinning fails with an error like `The requested Python version '3.14t' is incompatible with the project 'requires-python' value of '>=3.15'.` Edit `pyproject.toml` first, then continue.

## Pin the free-threaded build

```bash
uv python pin 3.14t
```

This writes `3.14t` into [`.python-version`](https://pydevtools.com/handbook/explanation/what-is-a-python-version-file.md). Every `uv run`, `uv sync`, and `uv add` in this project now uses the free-threaded interpreter.

## Rebuild the virtual environment

```bash
uv sync
```

uv detects the new pin, removes the old `.venv`, and recreates it against `3.14t`. Confirm the switch worked:

```bash
uv run python -c "import sys; print(sys._is_gil_enabled())"
```

The output should be `False`. `sys._is_gil_enabled()` was added in 3.13 so a script can report at runtime which build it is running on.

## Check dependencies for free-threaded wheels

Most pure-Python packages work unchanged. C extensions need a free-threaded wheel; otherwise uv falls back to building from source, which needs a compiler and can take minutes per package.

Watch `uv sync` output for lines that start with `Building` rather than `Downloading`. Those are source builds triggered by a missing free-threaded wheel:

```console
$ uv sync
   Building some-package==1.2.3
```

For a broader compatibility survey before you commit to the switch, check the [Quansight Labs free-threaded compatibility tracker](https://py-free-threading.github.io/tracking/). Popular scientific-Python packages (NumPy, pandas, SciPy, Pillow) ship free-threaded wheels on recent versions, but niche or unmaintained packages often do not.

When a dependency blocks the switch, options include:

- Wait for upstream. File or follow an upstream issue. Coverage has grown quickly across the 3.13 and 3.14 releases.
- Build from source. Acceptable for small pure-C extensions; painful for packages with heavy build toolchains.
- Isolate the dependency. Move the blocking code into a subprocess or service that runs on a GIL-enabled interpreter while the rest of your project stays free-threaded.

## Run your tests

```bash
uv run pytest
```

Free-threaded Python exposes thread-safety bugs that the GIL previously hid. Shared mutable state across threads now needs explicit locks, atomic primitives, or message passing. To surface these races, run your existing suite in a thread pool with [pytest-run-parallel](https://github.com/Quansight-Labs/pytest-run-parallel) or [pytest-freethreaded](https://github.com/tonybaloney/pytest-freethreaded), which execute each test across many threads at once. [pytest-xdist](https://pydevtools.com/handbook/how-to/how-to-run-tests-in-parallel-with-pytest-xdist.md) parallelizes across separate processes, so it speeds up the run but cannot expose the in-process data races free-threading introduces.

## Roll back if you need to

```bash
uv python pin 3.14
uv sync
```

This restores the standard GIL-enabled interpreter without touching `pyproject.toml` or the lockfile.

## Learn More

- [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.
- [Try Free-Threaded Python with uv](https://pydevtools.com/handbook/tutorial/try-free-threaded-python-with-uv.md) measures the speedup on a throwaway project
- [The official Python free-threading HOWTO](https://docs.python.org/3/howto/free-threading-python.html) covers identification, known limitations, and porting guidance
- [The Python Free-Threading Guide on validating thread safety](https://py-free-threading.github.io/testing/) walks through running an existing suite under threads to find races
- [PEP 703](https://pydevtools.com/handbook/explanation/what-is-pep-703.md) is the full proposal for making the GIL optional
- [Quansight Labs free-threaded compatibility tracker](https://py-free-threading.github.io/tracking/) shows which packages ship free-threaded wheels
