From b62567257961bb83bbd1c92e48d0018bf634cea6 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 27 Oct 2024 20:48:19 -0400 Subject: [PATCH] Clarify requires-python requirement for dependencies --- docs/concepts/resolution.md | 54 ++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/docs/concepts/resolution.md b/docs/concepts/resolution.md index 6fffda4fc44e..5ec45067a067 100644 --- a/docs/concepts/resolution.md +++ b/docs/concepts/resolution.md @@ -7,10 +7,10 @@ requirements of the requested packages are compatible. ## Dependencies -Most projects and packages have dependencies. Dependencies are other packages that are needed in +Most projects and packages have dependencies. Dependencies are other packages that are necessary in order for the current package to work. A package defines its dependencies as _requirements_, roughly a combination of a package name and acceptable versions. The dependencies defined by the current -project are called _direct dependencies_. The requirements added by each dependency of the current +project are called _direct dependencies_. The dependencies added by each dependency of the current project are called _indirect_ or _transitive dependencies_. !!! note @@ -34,10 +34,10 @@ To help demonstrate the resolution process, consider the following dependencies: In this example, the resolver must find a set of package versions which satisfies the project requirements. Since there is only one version of both `foo` and `bar`, those will be used. The resolution must also include the transitive dependencies, so a version of `lib` must be chosen. -`foo 1.0.0` allows all of the available versions of `lib`, but `bar 1.0.0` requires `lib>=2.0.0` so +`foo 1.0.0` allows all available versions of `lib`, but `bar 1.0.0` requires `lib>=2.0.0` so `lib 2.0.0` must be used. -In some resolutions, there is more than one solution. Consider the following dependencies: +In some resolutions, there may be more than one valid solution. Consider the following dependencies: - The project depends on `foo` and `bar`. @@ -49,21 +49,21 @@ In some resolutions, there is more than one solution. Consider the following dep - `bar 2.0.0` depends on `lib==1.0.0` - `lib` has two versions, 1.0.0 and 2.0.0. Both versions have no dependencies. -In this example, some version of both `foo` and `bar` must be picked, however, determining which +In this example, some version of both `foo` and `bar` must be selected; however, determining which version requires considering the dependencies of each version of `foo` and `bar`. `foo 2.0.0` and -`bar 2.0.0` cannot be installed together because they conflict on their required version of `lib`, -so the resolver must select either `foo 1.0.0` or `bar 1.0.0`. Both are valid solutions, and -different resolution algorithms may give either result. +`bar 2.0.0` cannot be installed together as they conflict on their required version of `lib`, so the +resolver must select either `foo 1.0.0` (along with `bar 2.0.0`) or `bar 1.0.0` (along with +`foo 1.0.0`). Both are valid solutions, and different resolution algorithms may yield either result. ## Platform markers Markers allow attaching an expression to requirements that indicate when the dependency should be -used. For example `bar; python_version<"3.9"` can be used to only require `bar` on Python 3.8 and -older. +used. For example `bar ; python_version < "3.9"` indicates that `bar` should only be installed on +Python 3.8 and earlier. -Markers are used to adjust a package's dependencies depending on the current environment or -platform. For example, markers can be used to change dependencies based on the operating system, the -CPU architecture, the Python version, the Python implementation, and more. +Markers are used to adjust a package's dependencies based on the current environment or platform. +For example, markers can be used to modify dependencies by operating system, CPU architecture, +Python version, Python implementation, and more. !!! note @@ -98,16 +98,20 @@ be used. A universal resolution is often more constrained than a platform-specif we need to take the requirements for all markers into account. During universal resolution, a minimum Python version must be specified. Project commands read the -minimum required version from `project.requires-python` in the `pyproject.toml`. When using the pip -interface, provide a value with the `--python-version` option, otherwise the current Python version -will be treated as a lower bound. For example, `--universal --python-version 3.9` writes a universal -resolution for Python 3.9 and later. +minimum required version from `project.requires-python` in the `pyproject.toml`. When using uv's pip +interface, provide a value with the `--python-version` option; otherwise, the current Python version +will be treated as a lower bound. For example, `--universal --python-version 3.9` performs a +universal resolution for Python 3.9 and later. -Setting the minimum Python version is important because all package versions we select have to be -compatible with the Python version range. For example, a universal resolution of `numpy<2` with -`--python-version 3.8` resolves to `numpy==1.24.4`, while `--python-version 3.9` resolves to -`numpy==1.26.4`, as `numpy` releases after 1.26.4 require at Python 3.9+. Note that we only consider -the lower bound of any Python requirement, upper bounds are always ignored. +During universal resolution, all selected dependency versions must be compatible with the _entire_ +`requires-python` range declared in the `pyproject.toml`. For example, if a project's +`requires-python` is `>=3.8`, then uv will not allow _any_ dependency versions that are limited to, +e.g., Python 3.9 and later, as they are not compatible with Python 3.8, the lower bound of the +project's supported range. In other words, the project's `requires-python` must be a subset of the +`requires-python` of all its dependencies. + +When evaluating `requires-python` ranges for dependencies, uv only considers lower bounds and +ignores upper bounds entirely. For example, `>=3.8, <4` is treated as `>=3.8`. ## Platform-specific resolution @@ -237,9 +241,9 @@ resolved versions, regardless of which packages are overlapping between the two. ## Dependency overrides -Dependency overrides allow bypassing failing or undesirable resolutions by overriding a package's -declared dependencies. Overrides are a useful last resort for cases in which you _know_ that a -dependency is compatible with a certain version of a package, despite the metadata indicating +Dependency overrides allow bypassing unsuccessful or undesirable resolutions by overriding a +package's declared dependencies. Overrides are a useful last resort for cases in which you _know_ +that a dependency is compatible with a certain version of a package, despite the metadata indicating otherwise. For example, if a transitive dependency declares the requirement `pydantic>=1.0,<2.0`, but _does_