uv/docs/concepts/projects/config.md
samsja 5ce48cf799
Some checks failed
CI / Determine changes (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / cargo shear (push) Has been cancelled
CI / typos (push) Has been cancelled
CI / mkdocs (push) Has been cancelled
CI / cargo clippy | ubuntu (push) Has been cancelled
CI / check system | conda3.8 on macos aarch64 (push) Has been cancelled
CI / cargo clippy | windows (push) Has been cancelled
CI / cargo dev generate-all (push) Has been cancelled
CI / cargo test | ubuntu (push) Has been cancelled
CI / cargo test | macos (push) Has been cancelled
CI / cargo test | windows (push) Has been cancelled
CI / check windows trampoline | aarch64 (push) Has been cancelled
CI / check windows trampoline | i686 (push) Has been cancelled
CI / check windows trampoline | x86_64 (push) Has been cancelled
CI / test windows trampoline | i686 (push) Has been cancelled
CI / test windows trampoline | x86_64 (push) Has been cancelled
CI / build binary | linux libc (push) Has been cancelled
CI / build binary | linux musl (push) Has been cancelled
CI / build binary | macos aarch64 (push) Has been cancelled
CI / build binary | macos x86_64 (push) Has been cancelled
CI / build binary | windows x86_64 (push) Has been cancelled
CI / build binary | windows aarch64 (push) Has been cancelled
CI / cargo build (msrv) (push) Has been cancelled
CI / build binary | freebsd (push) Has been cancelled
CI / smoke test | windows x86_64 (push) Has been cancelled
CI / ecosystem test | pydantic/pydantic-core (push) Has been cancelled
CI / ecosystem test | prefecthq/prefect (push) Has been cancelled
CI / ecosystem test | pallets/flask (push) Has been cancelled
CI / smoke test | linux (push) Has been cancelled
CI / smoke test | macos (push) Has been cancelled
CI / smoke test | windows aarch64 (push) Has been cancelled
CI / integration test | conda on ubuntu (push) Has been cancelled
CI / integration test | deadsnakes python3.9 on ubuntu (push) Has been cancelled
CI / integration test | free-threaded on linux (push) Has been cancelled
CI / integration test | free-threaded on windows (push) Has been cancelled
CI / integration test | pypy on ubuntu (push) Has been cancelled
CI / integration test | pypy on windows (push) Has been cancelled
CI / integration test | graalpy on ubuntu (push) Has been cancelled
CI / integration test | graalpy on windows (push) Has been cancelled
CI / integration test | github actions (push) Has been cancelled
CI / integration test | determine publish changes (push) Has been cancelled
CI / integration test | uv publish (push) Has been cancelled
CI / check cache | ubuntu (push) Has been cancelled
CI / check cache | macos aarch64 (push) Has been cancelled
CI / check system | python on debian (push) Has been cancelled
CI / check system | python on fedora (push) Has been cancelled
CI / check system | python on ubuntu (push) Has been cancelled
CI / check system | python on opensuse (push) Has been cancelled
CI / check system | python on rocky linux 8 (push) Has been cancelled
CI / check system | python on rocky linux 9 (push) Has been cancelled
CI / check system | pypy on ubuntu (push) Has been cancelled
CI / check system | pyston (push) Has been cancelled
CI / check system | alpine (push) Has been cancelled
CI / check system | python on macos aarch64 (push) Has been cancelled
CI / check system | homebrew python on macos aarch64 (push) Has been cancelled
CI / check system | python on macos x86-64 (push) Has been cancelled
CI / check system | python3.10 on windows x86-64 (push) Has been cancelled
CI / check system | python3.10 on windows x86 (push) Has been cancelled
CI / check system | python3.13 on windows x86-64 (push) Has been cancelled
CI / check system | windows registry (push) Has been cancelled
CI / check system | python3.12 via chocolatey (push) Has been cancelled
CI / check system | python3.9 via pyenv (push) Has been cancelled
CI / check system | python3.13 (push) Has been cancelled
CI / check system | conda3.11 on macos aarch64 (push) Has been cancelled
CI / check system | conda3.11 on linux x86-64 (push) Has been cancelled
CI / check system | conda3.8 on linux x86-64 (push) Has been cancelled
CI / check system | conda3.11 on windows x86-64 (push) Has been cancelled
CI / check system | conda3.8 on windows x86-64 (push) Has been cancelled
CI / check system | amazonlinux (push) Has been cancelled
CI / check system | embedded python3.10 on windows x86-64 (push) Has been cancelled
CI / benchmarks (push) Has been cancelled
add pip install setuptools in flash-attn docs (#11271)
## Summary

I followed the docs to install flash-attn with uv and got the following
issue
```uv sync
Resolved 27 packages in 12ms
  × Failed to build `flash-attn==2.7.4.post1`
  ├─▶ The build backend returned an error
  ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1)

      [stderr]
      Traceback (most recent call last):
        File "<string>", line 8, in <module>
      ModuleNotFoundError: No module named 'setuptools'

      hint: This usually indicates a problem with the package or the build environment.
```

installing setuptools before running uv sync as done with torch helps
fix it.
## Test Plan

I tested locally before it failed to install, after it worked
2025-02-05 20:59:56 -05:00

462 lines
16 KiB
Markdown

# Configuring projects
## 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 title="pyproject.toml" hl_lines="4"
[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).
## Entry points
[Entry points](https://packaging.python.org/en/latest/specifications/entry-points/#entry-points) are
the official term for an installed package to advertise interfaces. These include:
- [Command line interfaces](#command-line-interfaces)
- [Graphical user interfaces](#graphical-user-interfaces)
- [Plugin entry points](#plugin-entry-points)
!!! important
Using the entry point tables requires a [build system](#build-systems) 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 title="pyproject.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](#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 title="pyproject.toml"
[project.gui-scripts]
hello = "example:app"
```
### Plugin entry points
Projects may define entry points for plugin discovery in the
[`[project.entry-points]`](https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/#using-package-metadata)
table of the `pyproject.toml`.
For example, to register the `example-plugin-a` package as a plugin for `example`:
```toml title="pyproject.toml"
[project.entry-points.'example.plugins']
a = "example_plugin_a"
```
Then, in `example`, plugins would be loaded with:
```python title="example/__init__.py"
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 package or install it with `uv pip`,
uv will always attempt to build and install it.
### Build system options
Build systems are used to power the following features:
- Including or excluding files from distributions
- Editable install 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 [build systems](#build-systems), 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](#build-systems) to determine if a project
should be packaged, uv also allows overriding this behavior with the
[`tool.uv.package`](../../reference/settings.md#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.
## 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.
## 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 title="pyproject.toml"
[tool.uv]
environments = [
"sys_platform == 'darwin'",
"sys_platform == 'linux'",
]
```
Or, to exclude alternative Python implementations:
```toml title="pyproject.toml"
[tool.uv]
environments = [
"implementation_name == 'cpython'"
]
```
Entries in the `environments` setting must be disjoint (i.e., they must not overlap). For example,
`sys_platform == 'darwin'` and `sys_platform == 'linux'` are disjoint, but
`sys_platform == 'darwin'` and `python_version >= '3.9'` are not, since both could be true at the
same time.
## Build isolation
By default, uv builds all packages in isolated virtual environments, as per
[PEP 517](https://peps.python.org/pep-0517/). Some packages are incompatible with build isolation,
be it intentionally (e.g., due to the use of heavy build dependencies, mostly commonly PyTorch) or
unintentionally (e.g., due to the use of legacy packaging setups).
To disable build isolation for a specific dependency, add it to the `no-build-isolation-package`
list in your `pyproject.toml`:
```toml title="pyproject.toml"
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["cchardet"]
[tool.uv]
no-build-isolation-package = ["cchardet"]
```
Installing packages without build isolation requires that the package's build dependencies are
installed in the project environment _prior_ to installing the package itself. This can be achieved
by separating out the build dependencies and the packages that require them into distinct extras.
For example:
```toml title="pyproject.toml"
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[project.optional-dependencies]
build = ["setuptools", "cython"]
compile = ["cchardet"]
[tool.uv]
no-build-isolation-package = ["cchardet"]
```
Given the above, a user would first sync the `build` dependencies:
```console
$ uv sync --extra build
+ cython==3.0.11
+ foo==0.1.0 (from file:///Users/crmarsh/workspace/uv/foo)
+ setuptools==73.0.1
```
Followed by the `compile` dependencies:
```console
$ uv sync --extra compile
+ cchardet==2.1.7
- cython==3.0.11
- setuptools==73.0.1
```
Note that `uv sync --extra compile` would, by default, uninstall the `cython` and `setuptools`
packages. To instead retain the build dependencies, include both extras in the second `uv sync`
invocation:
```console
$ uv sync --extra build
$ uv sync --extra build --extra compile
```
Some packages, like `cchardet` above, only require build dependencies for the _installation_ phase
of `uv sync`. Others, like `flash-attn`, require their build dependencies to be present even just to
resolve the project's lockfile during the _resolution_ phase.
In such cases, the build dependencies must be installed prior to running any `uv lock` or `uv sync`
commands, using the lower lower-level `uv pip` API. For example, given:
```toml title="pyproject.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, you can provide the `flash-attn` metadata upfront via the
[`dependency-metadata`](../../reference/settings.md#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, include the following in your `pyproject.toml`:
```toml title="pyproject.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](https://pypi.org/project/flash-attn) 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.)
Once included, you can again use the two-step `uv sync` process to install the build dependencies.
Given the following `pyproject.toml`:
```toml title="pyproject.toml"
[project]
name = "project"
version = "0.1.0"
description = "..."
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[project.optional-dependencies]
build = ["torch", "setuptools", "packaging"]
compile = ["flash-attn"]
[tool.uv]
no-build-isolation-package = ["flash-attn"]
[[tool.uv.dependency-metadata]]
name = "flash-attn"
version = "2.6.3"
requires-dist = ["torch", "einops"]
```
You could run the following sequence of commands to sync `flash-attn`:
```console
$ uv sync --extra build
$ uv sync --extra build --extra compile
```
!!! note
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).
## 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 requires that all optional dependencies ("extras") declared by the project are compatible with
each other and resolves all optional dependencies together when creating the lockfile.
If optional dependencies declared in one extra are not compatible with those in another extra, uv
will fail to resolve the requirements of the project with an error.
To work around this, uv supports declaring conflicting extras. For example, consider two sets of
optional dependencies that conflict with one another:
```toml title="pyproject.toml"
[project.optional-dependencies]
extra1 = ["numpy==2.1.2"]
extra2 = ["numpy==2.0.0"]
```
If you run `uv lock` with the above dependencies, resolution will fail:
```console
$ uv lock
x No solution found when resolving dependencies:
`-> Because myproject[extra2] depends on numpy==2.0.0 and myproject[extra1] depends on numpy==2.1.2, we can conclude that myproject[extra1] and
myproject[extra2] are incompatible.
And because your project requires myproject[extra1] and myproject[extra2], we can conclude that your projects's requirements are unsatisfiable.
```
But if you specify that `extra1` and `extra2` are conflicting, uv will resolve them separately.
Specify conflicts in the `tool.uv` section:
```toml title="pyproject.toml"
[tool.uv]
conflicts = [
[
{ extra = "extra1" },
{ extra = "extra2" },
],
]
```
Now, running `uv lock` will succeed. Note though, that now you cannot install both `extra1` and
`extra2` at the same time:
```console
$ uv sync --extra extra1 --extra extra2
Resolved 3 packages in 14ms
error: extra `extra1`, extra `extra2` are incompatible with the declared conflicts: {`myproject[extra1]`, `myproject[extra2]`}
```
This error occurs because installing both `extra1` and `extra2` would result in installing two
different versions of a package into the same environment.
The above strategy for dealing with conflicting extras also works with dependency groups:
```toml title="pyproject.toml"
[dependency-groups]
group1 = ["numpy==2.1.2"]
group2 = ["numpy==2.0.0"]
[tool.uv]
conflicts = [
[
{ group = "group1" },
{ group = "group2" },
],
]
```
The only difference with conflicting extras is that you need to use `group` instead of `extra`.