# Project Configuration

## Python Version Requirement

Projects may declare the Python versions supported by the project in the `project.requires-python` field of the `pyproject.toml`.

It is recommended to set a `requires-python` value:

```toml
[project]
name = "example"
version = "0.1.0"
requires-python = ">=3.12"
```

The Python version requirement determines the Python syntax that is allowed in the project and affects selection of dependency versions (they must support the same Python version range). For details on managing Python versions, see `06-python-versions.md`.

## Entry Points

Entry points are the official term for an installed package to advertise interfaces. These include:

- Command line interfaces
- Graphical user interfaces
- Plugin entry points

**Important:** Using the entry point tables requires a build system to be defined.

### Command-line Interfaces

Projects may define command line interfaces (CLIs) for the project in the `[project.scripts]` table of the `pyproject.toml`.

For example, to declare a command called `hello` that invokes the `hello` function in the `example` module:

```toml
[project.scripts]
hello = "example:hello"
```

Then, the command can be run from a console:

```console
$ uv run hello
```

### Graphical User Interfaces

Projects may define graphical user interfaces (GUIs) for the project in the `[project.gui-scripts]` table of the `pyproject.toml`.

**Important:** These are only different from command-line interfaces on Windows, where they are wrapped by a GUI executable so they can be started without a console. On other platforms, they behave the same.

For example, to declare a command called `hello` that invokes the `app` function in the `example` module:

```toml
[project.gui-scripts]
hello = "example:app"
```

### Plugin Entry Points

Projects may define entry points for plugin discovery in the `[project.entry-points]` table of the `pyproject.toml`.

For example, to register the `example-plugin-a` package as a plugin for `example`:

```toml
[project.entry-points.'example.plugins']
a = "example_plugin_a"
```

Then, in `example`, plugins would be loaded with:

```python
from importlib.metadata import entry_points

for plugin in entry_points(group='example.plugins'):
    plugin.load()
```

**Note:** The `group` key can be an arbitrary value, it does not need to include the package name or "plugins". However, it is recommended to namespace the key by the package name to avoid collisions with other packages.

## Build Systems

A build system determines how the project should be packaged and installed. Projects may declare and configure a build system in the `[build-system]` table of the `pyproject.toml`.

uv uses the presence of a build system to determine if a project contains a package that should be installed in the project virtual environment. If a build system is not defined, uv will not attempt to build or install the project itself, just its dependencies. If a build system is defined, uv will build and install the project into the project environment.

The `--build-backend` option can be provided to `uv init` to create a packaged project with an appropriate layout. The `--package` option can be provided to `uv init` to create a packaged project with the default build system.

**Note:** While uv will not build and install the current project without a build system definition, the presence of a `[build-system]` table is not required in other packages. For legacy reasons, if a build system is not defined, then `setuptools.build_meta:__legacy__` is used to build the package. Packages you depend on may not explicitly declare their build system but are still installable. Similarly, if you add a dependency on a local project or install it with `uv pip`, uv will attempt to build and install it regardless of the presence of a `[build-system]` table.

Build systems are used to power the following features:

- Including or excluding files from distributions
- Editable installation behavior
- Dynamic project metadata
- Compilation of native code
- Vendoring shared libraries

To configure these features, refer to the documentation of your chosen build system.

## Project Packaging

As discussed in the build systems section, a Python project must be built to be installed. This process is generally referred to as "packaging".

You probably need a package if you want to:

- Add commands to the project
- Distribute the project to others
- Use a `src` and `test` layout
- Write a library

You probably _do not_ need a package if you are:

- Writing scripts
- Building a simple application
- Using a flat layout

While uv usually uses the declaration of a build system to determine if a project should be packaged, uv also allows overriding this behavior with the `tool.uv.package` setting.

Setting `tool.uv.package = true` will force a project to be built and installed into the project environment. If no build system is defined, uv will use the setuptools legacy backend.

Setting `tool.uv.package = false` will force a project package _not_ to be built and installed into the project environment. uv will ignore a declared build system when interacting with the project; however, uv will still respect explicit attempts to build the project such as invoking `uv build`.

## Project Environment Path

The `UV_PROJECT_ENVIRONMENT` environment variable can be used to configure the project virtual environment path (`.venv` by default).

If a relative path is provided, it will be resolved relative to the workspace root. If an absolute path is provided, it will be used as-is, i.e., a child directory will not be created for the environment. If an environment is not present at the provided path, uv will create it.

This option can be used to write to the system Python environment, though it is not recommended. `uv sync` will remove extraneous packages from the environment by default and, as such, may leave the system in a broken state.

To target the system environment, set `UV_PROJECT_ENVIRONMENT` to the prefix of the Python installation. For example, on Debian-based systems, this is usually `/usr/local`:

```console
$ python -c "import sysconfig; print(sysconfig.get_config_var('prefix'))"
/usr/local
```

To target this environment, you'd export `UV_PROJECT_ENVIRONMENT=/usr/local`.

**Important:** If an absolute path is provided and the setting is used across multiple projects, the environment will be overwritten by invocations in each project. This setting is only recommended for use for a single project in CI or Docker images.

**Note:** By default, uv does not read the `VIRTUAL_ENV` environment variable during project operations. A warning will be displayed if `VIRTUAL_ENV` is set to a different path than the project's environment. The `--active` flag can be used to opt-in to respecting `VIRTUAL_ENV`. The `--no-active` flag can be used to silence the warning.

## Build Isolation

By default, uv builds all packages in isolated virtual environments alongside their declared build dependencies, as per PEP 517. For information on building and publishing packages, see `08-building-and-publishing.md`.

Some packages are incompatible with this approach to build isolation, be it intentionally or unintentionally.

For example, packages like `flash-attn` and `deepspeed` need to build against the same version of PyTorch that is installed in the project environment; by building them in an isolated environment, they may inadvertently build against a different version of PyTorch, leading to runtime errors.

In other cases, packages may accidentally omit necessary dependencies in their declared build dependency list. For example, `cchardet` requires `cython` to be installed in the project environment prior to installing `cchardet`, but does not declare it as a build dependency.

To address these issues, uv supports two separate approaches to modifying the build isolation behavior:

1. **Augmenting the list of build dependencies**: This allows you to install a package in an isolated environment, but with additional build dependencies that are not declared by the package itself via the `extra-build-dependencies` setting. For packages like `flash-attn`, you can even enforce that those build dependencies (like `torch`) match the version of the package that is or will be installed in the project environment.

2. **Disabling build isolation for specific packages**: This allows you to install a package without building it in an isolated environment.

When possible, we recommend augmenting the build dependencies rather than disabling build isolation entirely, as the latter approach requires that the build dependencies are installed in the project environment _prior_ to installing the package itself, which can lead to more complex installation steps, the inclusion of extraneous packages in the project environment, and difficulty in reproducing the project environment in other contexts.

### Augmenting Build Dependencies

To augment the list of build dependencies for a specific package, add it to the `extra-build-dependencies` list in your `pyproject.toml`.

For example, to build `cchardet` with `cython` as an additional build dependency, include the following in your `pyproject.toml`:

```toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["cchardet"]

[tool.uv.extra-build-dependencies]
cchardet = ["cython"]
```

To ensure that a build dependency matches the version of the package that is or will be installed in the project environment, set `match-runtime = true` in the `extra-build-dependencies` table. For example, to build `deepspeed` with `torch` as an additional build dependency, include the following in your `pyproject.toml`:

```toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["deepspeed", "torch"]

[tool.uv.extra-build-dependencies]
deepspeed = [{ requirement = "torch", match-runtime = true }]
```

This will ensure that `deepspeed` is built with the same version of `torch` that is installed in the project environment.

Similarly, to build `flash-attn` with `torch` as an additional build dependency, include the following in your `pyproject.toml`:

```toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["flash-attn", "torch"]

[tool.uv.extra-build-dependencies]
flash-attn = [{ requirement = "torch", match-runtime = true }]

[tool.uv.extra-build-variables]
flash-attn = { FLASH_ATTENTION_SKIP_CUDA_BUILD = "TRUE" }
```

**Note:** The `FLASH_ATTENTION_SKIP_CUDA_BUILD` environment variable ensures that `flash-attn` is installed from a compatible, pre-built wheel, rather than attempting to build it from source, which requires access to the CUDA development toolkit. If the CUDA toolkit is not available, the environment variable can be omitted, and `flash-attn` will be installed from a pre-built wheel if one is available for the current platform, Python version, and PyTorch version.

Similarly, `deep_gemm` follows the same pattern:

```toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["deep_gemm", "torch"]

[tool.uv.sources]
deep_gemm = { git = "https://github.com/deepseek-ai/DeepGEMM" }

[tool.uv.extra-build-dependencies]
deep_gemm = [{ requirement = "torch", match-runtime = true }]
```

The use of `extra-build-dependencies` and `extra-build-variables` are tracked in the uv cache, such that changes to these settings will trigger a reinstall and rebuild of the affected packages. For example, in the case of `flash-attn`, upgrading the version of `torch` used in your project would subsequently trigger a rebuild of `flash-attn` with the new version of `torch`.

#### Dynamic Metadata

The use of `match-runtime = true` is only available for packages like `flash-attn` that declare static metadata. If static metadata is unavailable, uv is required to build the package during the dependency resolution phase; as such, uv cannot determine the version of the build dependency that would ultimately be installed in the project environment.

In other words, if `flash-attn` did not declare static metadata, uv would not be able to determine the version of `torch` that would be installed in the project environment, since it would need to build `flash-attn` prior to resolving the `torch` version.

As a concrete example, `axolotl` is a popular package that requires augmented build dependencies, but does not declare static metadata, as the package's dependencies vary based on the version of `torch` that is installed in the project environment. In this case, users should instead specify the exact version of `torch` that they intend to use in their project, and then augment the build dependencies with that version.

For example, to build `axolotl` against `torch==2.6.0`, include the following in your `pyproject.toml`:

```toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["axolotl[deepspeed, flash-attn]", "torch==2.6.0"]

[tool.uv.extra-build-dependencies]
axolotl = ["torch==2.6.0"]
deepspeed = ["torch==2.6.0"]
flash-attn = ["torch==2.6.0"]
```

Similarly, older versions of `flash-attn` did not declare static metadata, and thus would not have supported `match-runtime = true` out of the box. Unlike `axolotl`, though, `flash-attn` did not vary its dependencies based on dynamic properties of the build environment. As such, users could instead provide the `flash-attn` metadata upfront via the `dependency-metadata` setting, thereby forgoing the need to build the package during the dependency resolution phase. For example, to provide the `flash-attn` metadata upfront:

```toml
[[tool.uv.dependency-metadata]]
name = "flash-attn"
version = "2.6.3"
requires-dist = ["torch", "einops"]
```

**Tip:** To determine the package metadata for a package like `flash-attn`, navigate to the appropriate Git repository, or look it up on PyPI and download the package's source distribution. The package requirements can typically be found in the `setup.py` or `setup.cfg` file.

(If the package includes a built distribution, you can unzip it to find the `METADATA` file; however, the presence of a built distribution would negate the need to provide the metadata upfront, since it would already be available to uv.)

The `version` field in `tool.uv.dependency-metadata` is optional for registry-based dependencies (when omitted, uv will assume the metadata applies to all versions of the package), but _required_ for direct URL dependencies (like Git dependencies).

### Disabling Build Isolation

Installing packages without build isolation requires that the package's build dependencies are installed in the project environment _prior_ to building the package itself.

For example, historically, to install `cchardet` without build isolation, you would first need to install the `cython` and `setuptools` packages in the project environment, followed by a separate invocation to install `cchardet` without build isolation:

```console
$ uv venv
$ uv pip install cython setuptools
$ uv pip install cchardet --no-build-isolation
```

uv simplifies this process by allowing you to specify packages that should not be built in isolation via the `no-build-isolation-package` setting in your `pyproject.toml` and the `--no-build-isolation-package` flag in the command line. Further, when a package is marked for disabling build isolation, uv will perform a two-phase install, first installing any packages that support build isolation, followed by those that do not. As a result, if a project's build dependencies are included as project dependencies, uv will automatically install them before installing the package that requires build isolation to be disabled.

For example, to install `cchardet` without build isolation, include the following in your `pyproject.toml`:

```toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["cchardet", "cython", "setuptools"]

[tool.uv]
no-build-isolation-package = ["cchardet"]
```

When running `uv sync`, uv will first install `cython` and `setuptools` in the project environment, followed by `cchardet` (without build isolation):

```console
$ uv sync --extra build
 + cchardet==2.1.7
 + cython==3.1.3
 + setuptools==80.9.0
```

Similarly, to install `flash-attn` without build isolation, include the following in your `pyproject.toml`:

```toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["flash-attn", "torch"]

[tool.uv]
no-build-isolation-package = ["flash-attn"]
```

When running `uv sync`, uv will first install `torch` in the project environment, followed by `flash-attn` (without build isolation). As `torch` is both a project dependency and a build dependency, the version of `torch` is guaranteed to be consistent between the build and runtime environments.

A downside of the above approach is that it requires the build dependencies to be installed in the project environment, which is appropriate for `flash-attn` (which requires `torch` both at build-time and runtime), but not for `cchardet` (which only requires `cython` at build-time).

To avoid including build dependencies in the project environment, uv supports a two-step installation process that allows you to separate the build dependencies from the packages that require them.

For example, the build dependencies for `cchardet` can be isolated to an optional `build` group, as in:

```toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["cchardet"]

[project.optional-dependencies]
build = ["setuptools", "cython"]

[tool.uv]
no-build-isolation-package = ["cchardet"]
```

Given the above, a user would first sync with the `build` optional group, and then without it to remove the build dependencies:

```console
$ uv sync --extra build
 + cchardet==2.1.7
 + cython==3.1.3
 + setuptools==80.9.0
$ uv sync
 - cython==3.1.3
 - setuptools==80.9.0
```

Some packages, like `cchardet`, only require build dependencies for the _installation_ phase of `uv sync`. Others require their build dependencies to be present even just to resolve the project's dependencies during the _resolution_ phase.

In such cases, the build dependencies can be installed prior to running any `uv lock` or `uv sync` commands, using the lower-level `uv pip` API. For example, given:

```toml
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["flash-attn"]

[tool.uv]
no-build-isolation-package = ["flash-attn"]
```

You could run the following sequence of commands to sync `flash-attn`:

```console
$ uv venv
$ uv pip install torch setuptools
$ uv sync
```

Alternatively, users can instead provide the `flash-attn` metadata upfront via the `dependency-metadata` setting, thereby forgoing the need to build the package during the dependency resolution phase. For example, to provide the `flash-attn` metadata upfront:

```toml
[[tool.uv.dependency-metadata]]
name = "flash-attn"
version = "2.6.3"
requires-dist = ["torch", "einops"]
```

## Editable Mode

By default, the project will be installed in editable mode, such that changes to the source code are immediately reflected in the environment. `uv sync` and `uv run` both accept a `--no-editable` flag, which instructs uv to install the project in non-editable mode. `--no-editable` is intended for deployment use-cases, such as building a Docker container, in which the project should be included in the deployed environment without a dependency on the originating source code.

## Conflicting Dependencies

uv resolves all project dependencies together, including optional dependencies ("extras") and dependency groups. If dependencies declared in one section are not compatible with those in another section, uv will fail to resolve the requirements of the project with an error.

uv supports explicit declaration of conflicting dependency groups. For details on resolution and conflicts, see `05-resolution.md`. For example, to declare that the `optional-dependency` groups `extra1` and `extra2` are incompatible:

```toml
[tool.uv]
conflicts = [
    [
      { extra = "extra1" },
      { extra = "extra2" },
    ],
]
```

Or, to declare the development dependency groups `group1` and `group2` incompatible:

```toml
[tool.uv]
conflicts = [
    [
      { group = "group1" },
      { group = "group2" },
    ],
]
```

## Limited Resolution Environments

If your project supports a more limited set of platforms or Python versions, you can constrain the set of solved platforms via the `environments` setting, which accepts a list of PEP 508 environment markers. For example, to constrain the lockfile to macOS and Linux, and exclude Windows:

```toml
[tool.uv]
environments = [
    "sys_platform == 'darwin'",
    "sys_platform == 'linux'",
]
```

## Required Environments

If your project _must_ support a specific platform or Python version, you can mark that platform as required via the `required-environments` setting. For example, to require that the project supports Intel macOS:

```toml
[tool.uv]
required-environments = [
    "sys_platform == 'darwin' and platform_machine == 'x86_64'",
]
```

The `required-environments` setting is only relevant for packages that do not publish a source distribution (like PyTorch), as such packages can _only_ be installed on environments covered by the set of pre-built binary distributions (wheels) published by that package.

## See Also

- `02-projects.md` - Project creation, running, and syncing
- `04-dependencies.md` - Dependency management, groups, and sources
- `05-resolution.md` - Resolution strategies, overrides, and constraints
- `06-python-versions.md` - Python version management
- `08-building-and-publishing.md` - Building and publishing packages
- `10-configuration.md` - Config files, indexes, cache, and storage
