Files
uv/crates/uv-resolver/src/python_requirement.rs
T

110 lines
3.9 KiB
Rust
Raw Normal View History

use pep440_rs::VersionSpecifiers;
use pep508_rs::StringVersion;
use uv_toolchain::{Interpreter, PythonVersion};
2024-06-06 19:45:58 -04:00
use crate::RequiresPython;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PythonRequirement {
/// The installed version of Python.
installed: StringVersion,
/// 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.
///
/// If `None`, the target version is the same as the installed version.
2024-06-06 19:45:58 -04:00
target: Option<PythonTarget>,
}
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 {
Self {
installed: interpreter.python_full_version().clone(),
2024-06-06 19:45:58 -04:00
target: Some(PythonTarget::Version(StringVersion {
string: python_version.to_string(),
version: python_version.python_full_version(),
})),
}
}
/// Create a [`PythonRequirement`] to resolve against both an [`Interpreter`] and a
/// [`MarkerEnvironment`].
pub fn from_requires_python(
interpreter: &Interpreter,
2024-06-06 19:45:58 -04:00
requires_python: &RequiresPython,
) -> Self {
Self {
installed: interpreter.python_full_version().clone(),
2024-06-06 19:45:58 -04:00
target: Some(PythonTarget::RequiresPython(requires_python.clone())),
}
}
/// Create a [`PythonRequirement`] to resolve against an [`Interpreter`].
2024-06-02 21:33:18 -04:00
pub fn from_interpreter(interpreter: &Interpreter) -> Self {
Self {
installed: interpreter.python_full_version().clone(),
target: None,
2024-06-02 21:33:18 -04:00
}
}
/// Return the installed version of Python.
pub fn installed(&self) -> &StringVersion {
&self.installed
}
/// Return the target version of Python.
2024-06-06 19:45:58 -04:00
pub fn target(&self) -> Option<&PythonTarget> {
self.target.as_ref()
}
/// Return the target version of Python as a "requires python" type,
/// if available.
pub(crate) fn requires_python(&self) -> Option<&RequiresPython> {
self.target().and_then(|target| target.as_requires_python())
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
2024-06-06 19:45:58 -04:00
pub enum PythonTarget {
/// The [`PythonTarget`] specifier is a single version specifier, as provided via
/// `--python-version` on the command line.
///
/// The use of a separate enum variant allows us to use a verbatim representation when reporting
/// back to the user.
2024-06-06 19:45:58 -04:00
Version(StringVersion),
/// The [`PythonTarget`] specifier is a set of version specifiers, as extracted from the
/// `Requires-Python` field in a `pyproject.toml` or `METADATA` file.
2024-06-06 19:45:58 -04:00
RequiresPython(RequiresPython),
}
2024-06-06 19:45:58 -04:00
impl PythonTarget {
/// Returns `true` if the target Python is compatible with the [`VersionSpecifiers`].
pub fn is_compatible_with(&self, target: &VersionSpecifiers) -> bool {
match self {
2024-06-06 19:45:58 -04:00
PythonTarget::Version(version) => target.contains(version),
PythonTarget::RequiresPython(requires_python) => {
requires_python.is_contained_by(target)
}
}
}
2024-06-06 19:45:58 -04:00
/// Returns the [`RequiresPython`] for the [`PythonTarget`] specifier.
pub fn as_requires_python(&self) -> Option<&RequiresPython> {
match self {
2024-06-06 19:45:58 -04:00
PythonTarget::Version(_) => None,
PythonTarget::RequiresPython(requires_python) => Some(requires_python),
}
}
}
2024-06-06 19:45:58 -04:00
impl std::fmt::Display for PythonTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
2024-06-06 19:45:58 -04:00
PythonTarget::Version(specifier) => std::fmt::Display::fmt(specifier, f),
PythonTarget::RequiresPython(specifiers) => std::fmt::Display::fmt(specifiers, f),
}
}
}