Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trim injected python_version marker to (major, minor) #2395

Merged
merged 3 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/pep508-rs/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ impl FromStr for StringVersion {

impl Display for StringVersion {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.version.fmt(f)
self.string.fmt(f)
}
}

Expand Down
8 changes: 7 additions & 1 deletion crates/uv-interpreter/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use tracing::{debug, warn};
use cache_key::digest;
use install_wheel_rs::Layout;
use pep440_rs::Version;
use pep508_rs::MarkerEnvironment;
use pep508_rs::{MarkerEnvironment, StringVersion};
use platform_host::Platform;
use platform_tags::{Tags, TagsError};
use pypi_types::Scheme;
Expand Down Expand Up @@ -182,6 +182,12 @@ impl Interpreter {
&self.markers.python_full_version.version
}

/// Returns the `python_full_version` marker corresponding to this Python version.
#[inline]
pub const fn python_full_version(&self) -> &StringVersion {
&self.markers.python_full_version
}

/// Return the major version of this Python version.
pub fn python_major(&self) -> u8 {
let major = self.markers.python_full_version.version.release()[0];
Expand Down
115 changes: 96 additions & 19 deletions crates/uv-interpreter/src/python_version.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use pep440_rs::Version;
use pep508_rs::{MarkerEnvironment, StringVersion};
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::str::FromStr;

use pep440_rs::Version;
use pep508_rs::{MarkerEnvironment, StringVersion};

use crate::Interpreter;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -59,43 +60,71 @@ impl PythonVersion {

// Ex) `implementation_version == "3.12.0"`
if markers.implementation_name == "cpython" {
markers.implementation_version = self.0.clone();
let python_full_version = self.python_full_version();
markers.implementation_version = StringVersion {
// Retain the verbatim representation, provided by the user.
string: self.0.to_string(),
version: python_full_version,
};
}

// Ex) `python_full_version == "3.12.0"`
markers.python_full_version = self.0.clone();
let python_full_version = self.python_full_version();
markers.python_full_version = StringVersion {
// Retain the verbatim representation, provided by the user.
string: self.0.to_string(),
version: python_full_version,
};

// Ex) `python_version == "3.12"`
markers.python_version = self.0;
let python_version = self.python_version();
markers.python_version = StringVersion {
string: python_version.to_string(),
version: python_version,
};

markers
}

/// Return the `python_version` marker corresponding to this Python version.
///
/// This should include exactly a major and minor version, but no patch version.
///
/// Ex) `python_version == "3.12"`
pub fn python_version(&self) -> Version {
let major = self.release().first().copied().unwrap_or(0);
let minor = self.release().get(1).copied().unwrap_or(0);
Version::new([major, minor])
}

/// Return the `python_full_version` marker corresponding to this Python version.
///
/// This should include exactly a major, minor, and patch version (even if it's zero), along
/// with any pre-release or post-release information.
///
/// Ex) `python_full_version == "3.12.0b1"`
pub fn python_full_version(&self) -> Version {
let major = self.release().first().copied().unwrap_or(0);
let minor = self.release().get(1).copied().unwrap_or(0);
let patch = self.release().get(2).copied().unwrap_or(0);
Version::new([major, minor, patch])
.with_pre(self.0.pre())
.with_post(self.0.post())
}

/// Return the full parsed Python version.
pub fn version(&self) -> &Version {
&self.0.version
}

/// Return the major version of this Python version.
pub fn major(&self) -> u8 {
u8::try_from(self.0.release()[0]).expect("invalid major version")
u8::try_from(self.0.release().first().copied().unwrap_or(0)).expect("invalid major version")
}

/// Return the minor version of this Python version.
pub fn minor(&self) -> u8 {
u8::try_from(self.0.release()[1]).expect("invalid minor version")
}

/// Check if this Python version is satisfied by the given interpreter.
///
/// If a patch version is present, we will require an exact match.
/// Otherwise, just the major and minor version numbers need to match.
pub fn is_satisfied_by(&self, interpreter: &Interpreter) -> bool {
if self.patch().is_some() {
self.version() == interpreter.python_version()
} else {
(self.major(), self.minor()) == interpreter.python_tuple()
}
u8::try_from(self.0.release().get(1).copied().unwrap_or(0)).expect("invalid minor version")
}

/// Return the patch version of this Python version, if set.
Expand All @@ -113,4 +142,52 @@ impl PythonVersion {
Self::from_str(format!("{}.{}", self.major(), self.minor()).as_str())
.expect("dropping a patch should always be valid")
}

/// Check if this Python version is satisfied by the given interpreter.
///
/// If a patch version is present, we will require an exact match.
/// Otherwise, just the major and minor version numbers need to match.
pub fn is_satisfied_by(&self, interpreter: &Interpreter) -> bool {
if self.patch().is_some() {
self.version() == interpreter.python_version()
} else {
(self.major(), self.minor()) == interpreter.python_tuple()
}
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;

use pep440_rs::{PreRelease, PreReleaseKind, Version};

use crate::PythonVersion;

#[test]
fn python_markers() {
let version = PythonVersion::from_str("3.11.0").expect("valid python version");
assert_eq!(version.python_version(), Version::new([3, 11]));
assert_eq!(version.python_version().to_string(), "3.11");
assert_eq!(version.python_full_version(), Version::new([3, 11, 0]));
assert_eq!(version.python_full_version().to_string(), "3.11.0");

let version = PythonVersion::from_str("3.11").expect("valid python version");
assert_eq!(version.python_version(), Version::new([3, 11]));
assert_eq!(version.python_version().to_string(), "3.11");
assert_eq!(version.python_full_version(), Version::new([3, 11, 0]));
assert_eq!(version.python_full_version().to_string(), "3.11.0");

let version = PythonVersion::from_str("3.11.8a1").expect("valid python version");
assert_eq!(version.python_version(), Version::new([3, 11]));
assert_eq!(version.python_version().to_string(), "3.11");
assert_eq!(
version.python_full_version(),
Version::new([3, 11, 8]).with_pre(Some(PreRelease {
kind: PreReleaseKind::Alpha,
number: 1
}))
);
assert_eq!(version.python_full_version().to_string(), "3.11.8a1");
}
}
5 changes: 3 additions & 2 deletions crates/uv-resolver/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::BTreeSet;
use std::convert::Infallible;
use std::fmt::Formatter;
use std::ops::Deref;

use dashmap::{DashMap, DashSet};
use indexmap::IndexMap;
Expand Down Expand Up @@ -190,13 +191,13 @@ impl NoSolutionError {
PubGrubPackage::Python(PubGrubPython::Installed) => {
available_versions.insert(
package.clone(),
BTreeSet::from([python_requirement.installed().clone()]),
BTreeSet::from([python_requirement.installed().deref().clone()]),
);
}
PubGrubPackage::Python(PubGrubPython::Target) => {
available_versions.insert(
package.clone(),
BTreeSet::from([python_requirement.target().clone()]),
BTreeSet::from([python_requirement.target().deref().clone()]),
);
}
PubGrubPackage::Package(name, ..) => {
Expand Down
17 changes: 8 additions & 9 deletions crates/uv-resolver/src/python_requirement.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
use pep440_rs::Version;
use pep508_rs::MarkerEnvironment;
use pep508_rs::{MarkerEnvironment, StringVersion};
use uv_interpreter::Interpreter;

#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PythonRequirement {
/// The installed version of Python.
installed: Version,
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.
target: Version,
target: StringVersion,
}

impl PythonRequirement {
pub fn new(interpreter: &Interpreter, markers: &MarkerEnvironment) -> Self {
Self {
installed: interpreter.python_version().clone(),
target: markers.python_full_version.version.clone(),
installed: interpreter.python_full_version().clone(),
target: markers.python_full_version.clone(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same value, I just renamed the method because it's the python_full_version marker.

}
}

/// Return the installed version of Python.
pub fn installed(&self) -> &Version {
pub fn installed(&self) -> &StringVersion {
&self.installed
}

/// Return the target version of Python.
pub fn target(&self) -> &Version {
pub fn target(&self) -> &StringVersion {
&self.target
}
}
5 changes: 3 additions & 2 deletions crates/uv-resolver/src/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::borrow::Cow;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::sync::Arc;

use anyhow::Result;
Expand Down Expand Up @@ -554,7 +555,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
PubGrubPackage::Python(PubGrubPython::Installed) => {
let version = self.python_requirement.installed();
if range.contains(version) {
Ok(Some(ResolverVersion::Available(version.clone())))
Ok(Some(ResolverVersion::Available(version.deref().clone())))
} else {
Ok(None)
}
Expand All @@ -563,7 +564,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
PubGrubPackage::Python(PubGrubPython::Target) => {
let version = self.python_requirement.target();
if range.contains(version) {
Ok(Some(ResolverVersion::Available(version.clone())))
Ok(Some(ResolverVersion::Available(version.deref().clone())))
} else {
Ok(None)
}
Expand Down
Loading