mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-01 08:17:24 +00:00

The net effect here is (I think) just that we end up logging more "narrowing" outcomes than we have in practice.
187 lines
6.3 KiB
Rust
187 lines
6.3 KiB
Rust
use std::collections::Bound;
|
|
|
|
use uv_pep440::Version;
|
|
use uv_pep508::{MarkerEnvironment, MarkerTree};
|
|
use uv_python::{Interpreter, PythonVersion};
|
|
|
|
use crate::{RequiresPython, RequiresPythonRange};
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
pub struct PythonRequirement {
|
|
source: PythonRequirementSource,
|
|
/// The exact installed version of Python.
|
|
exact: Version,
|
|
/// The installed version of Python.
|
|
installed: RequiresPython,
|
|
/// The target version of Python; that is, the version of Python for which we are resolving
|
|
/// dependencies. This is typically the same as the installed version, but may be different
|
|
/// when specifying an alternate Python version for the resolution.
|
|
target: RequiresPython,
|
|
}
|
|
|
|
impl PythonRequirement {
|
|
/// Create a [`PythonRequirement`] to resolve against both an [`Interpreter`] and a
|
|
/// [`PythonVersion`].
|
|
pub fn from_python_version(interpreter: &Interpreter, python_version: &PythonVersion) -> Self {
|
|
let exact = interpreter.python_full_version().version.clone();
|
|
let installed = interpreter
|
|
.python_full_version()
|
|
.version
|
|
.only_release()
|
|
.without_trailing_zeros();
|
|
let target = python_version
|
|
.python_full_version()
|
|
.only_release()
|
|
.without_trailing_zeros();
|
|
Self {
|
|
exact,
|
|
installed: RequiresPython::greater_than_equal_version(&installed),
|
|
target: RequiresPython::greater_than_equal_version(&target),
|
|
source: PythonRequirementSource::PythonVersion,
|
|
}
|
|
}
|
|
|
|
/// Create a [`PythonRequirement`] to resolve against both an [`Interpreter`] and a
|
|
/// [`MarkerEnvironment`].
|
|
pub fn from_requires_python(
|
|
interpreter: &Interpreter,
|
|
requires_python: RequiresPython,
|
|
) -> Self {
|
|
Self::from_marker_environment(interpreter.markers(), requires_python)
|
|
}
|
|
|
|
/// Create a [`PythonRequirement`] to resolve against an [`Interpreter`].
|
|
pub fn from_interpreter(interpreter: &Interpreter) -> Self {
|
|
let exact = interpreter
|
|
.python_full_version()
|
|
.version
|
|
.clone()
|
|
.without_trailing_zeros();
|
|
let installed = interpreter
|
|
.python_full_version()
|
|
.version
|
|
.only_release()
|
|
.without_trailing_zeros();
|
|
Self {
|
|
exact,
|
|
installed: RequiresPython::greater_than_equal_version(&installed),
|
|
target: RequiresPython::greater_than_equal_version(&installed),
|
|
source: PythonRequirementSource::Interpreter,
|
|
}
|
|
}
|
|
|
|
/// Create a [`PythonRequirement`] from a [`MarkerEnvironment`] and a
|
|
/// specific `Requires-Python` directive.
|
|
///
|
|
/// This has the same "source" as
|
|
/// [`PythonRequirement::from_requires_python`], but is useful for
|
|
/// constructing a `PythonRequirement` without an [`Interpreter`].
|
|
pub fn from_marker_environment(
|
|
marker_env: &MarkerEnvironment,
|
|
requires_python: RequiresPython,
|
|
) -> Self {
|
|
let exact = marker_env
|
|
.python_full_version()
|
|
.version
|
|
.clone()
|
|
.without_trailing_zeros();
|
|
let installed = marker_env
|
|
.python_full_version()
|
|
.version
|
|
.only_release()
|
|
.without_trailing_zeros();
|
|
Self {
|
|
exact,
|
|
installed: RequiresPython::greater_than_equal_version(&installed),
|
|
target: requires_python,
|
|
source: PythonRequirementSource::RequiresPython,
|
|
}
|
|
}
|
|
|
|
/// Narrow the [`PythonRequirement`] to the given version, if it's stricter (i.e., greater)
|
|
/// than the current `Requires-Python` minimum.
|
|
///
|
|
/// Returns `None` if the given range is not narrower than the current range.
|
|
pub fn narrow(&self, target: &RequiresPythonRange) -> Option<Self> {
|
|
Some(Self {
|
|
exact: self.exact.clone(),
|
|
installed: self.installed.clone(),
|
|
target: self.target.narrow(target)?,
|
|
source: self.source,
|
|
})
|
|
}
|
|
|
|
/// Split the [`PythonRequirement`] at the given version.
|
|
///
|
|
/// For example, if the current requirement is `>=3.10`, and the split point is `3.11`, then
|
|
/// the result will be `>=3.10 and <3.11` and `>=3.11`.
|
|
pub fn split(&self, at: Bound<Version>) -> Option<(Self, Self)> {
|
|
let (lower, upper) = self.target.split(at)?;
|
|
Some((
|
|
Self {
|
|
exact: self.exact.clone(),
|
|
installed: self.installed.clone(),
|
|
target: lower,
|
|
source: self.source,
|
|
},
|
|
Self {
|
|
exact: self.exact.clone(),
|
|
installed: self.installed.clone(),
|
|
target: upper,
|
|
source: self.source,
|
|
},
|
|
))
|
|
}
|
|
|
|
/// Returns `true` if the minimum version of Python required by the target is greater than the
|
|
/// installed version.
|
|
pub fn raises(&self, target: &RequiresPythonRange) -> bool {
|
|
target.lower() > self.target.range().lower()
|
|
}
|
|
|
|
/// Return the exact version of Python.
|
|
pub fn exact(&self) -> &Version {
|
|
&self.exact
|
|
}
|
|
|
|
/// Return the installed version of Python.
|
|
pub fn installed(&self) -> &RequiresPython {
|
|
&self.installed
|
|
}
|
|
|
|
/// Return the target version of Python.
|
|
pub fn target(&self) -> &RequiresPython {
|
|
&self.target
|
|
}
|
|
|
|
/// Return the source of the [`PythonRequirement`].
|
|
pub fn source(&self) -> PythonRequirementSource {
|
|
self.source
|
|
}
|
|
|
|
/// A wrapper around `RequiresPython::simplify_markers`. See its docs for
|
|
/// more info.
|
|
///
|
|
/// When this `PythonRequirement` isn't `RequiresPython`, the given markers
|
|
/// are returned unchanged.
|
|
pub(crate) fn simplify_markers(&self, marker: MarkerTree) -> MarkerTree {
|
|
self.target.simplify_markers(marker)
|
|
}
|
|
|
|
/// Return a [`MarkerTree`] representing the Python requirement.
|
|
///
|
|
/// See: [`RequiresPython::to_marker_tree`]
|
|
pub fn to_marker_tree(&self) -> MarkerTree {
|
|
self.target.to_marker_tree()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)]
|
|
pub enum PythonRequirementSource {
|
|
/// `--python-version`
|
|
PythonVersion,
|
|
/// `Requires-Python`
|
|
RequiresPython,
|
|
/// The discovered Python interpreter.
|
|
Interpreter,
|
|
}
|