diff --git a/crates/uv-resolver/src/requires_python.rs b/crates/uv-resolver/src/requires_python.rs index e820b31c1..f671b8ae2 100644 --- a/crates/uv-resolver/src/requires_python.rs +++ b/crates/uv-resolver/src/requires_python.rs @@ -19,22 +19,16 @@ pub enum RequiresPythonError { /// The `Requires-Python` requirement specifier. /// -/// We treat `Requires-Python` as a lower bound. For example, if the requirement expresses -/// `>=3.8, <4`, we treat it as `>=3.8`. `Requires-Python` itself was intended to enable -/// packages to drop support for older versions of Python without breaking installations on -/// those versions, and packages cannot know whether they are compatible with future, unreleased -/// versions of Python. -/// /// See: #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct RequiresPython { /// The supported Python versions as provides by the user, usually through the `requires-python` /// field in `pyproject.toml`. /// - /// For a workspace, it's the union of all `requires-python` values in the workspace. If no + /// For a workspace, it's the intersection of all `requires-python` values in the workspace. If no /// bound was provided by the user, it's greater equal the current Python version. specifiers: VersionSpecifiers, - /// The lower and upper bounds of `specifiers`. + /// The lower and upper bounds of the given specifiers. range: RequiresPythonRange, } @@ -108,7 +102,7 @@ impl RequiresPython { })) } - /// Narrow the [`RequiresPython`] to the given version, if it's stricter than the current target. + /// Narrow the [`RequiresPython`] by computing the intersection with the given range. pub fn narrow(&self, range: &RequiresPythonRange) -> Option { let lower = if range.0 >= self.range.0 { Some(&range.0) @@ -228,17 +222,35 @@ impl RequiresPython { } /// Returns `true` if the `Requires-Python` is compatible with the given version. + /// + /// N.B. This operation should primarily be used when evaluating compatibility of Python + /// versions against the user's own project. For example, if the user defines a + /// `requires-python` in a `pyproject.toml`, this operation could be used to determine whether + /// a given Python interpreter is compatible with the user's project. pub fn contains(&self, version: &Version) -> bool { let version = version.only_release(); self.specifiers.contains(&version) } - /// Returns `true` if the `Requires-Python` is compatible with the given version specifiers. + /// Returns `true` if the `Requires-Python` is contained by the given version specifiers. + /// + /// In this context, we treat `Requires-Python` as a lower bound. For example, if the + /// requirement expresses `>=3.8, <4`, we treat it as `>=3.8`. `Requires-Python` itself was + /// intended to enable packages to drop support for older versions of Python without breaking + /// installations on those versions, and packages cannot know whether they are compatible with + /// future, unreleased versions of Python. + /// + /// The specifiers are considered to "contain" the `Requires-Python` if the specifiers are + /// compatible with all versions in the `Requires-Python` range (i.e., have a _lower_ lower + /// bound). /// /// For example, if the `Requires-Python` is `>=3.8`, then `>=3.7` would be considered /// compatible, since all versions in the `Requires-Python` range are also covered by the /// provided range. However, `>=3.9` would not be considered compatible, as the /// `Requires-Python` includes Python 3.8, but `>=3.9` does not. + /// + /// N.B. This operation should primarily be used when evaluating the compatibility of a + /// project's `Requires-Python` specifier against a dependency's `Requires-Python` specifier. pub fn is_contained_by(&self, target: &VersionSpecifiers) -> bool { let Ok(target) = VersionRangesSpecifier::from_release_specifiers(target) else { return false;