diff --git a/crates/puffin-cli/src/commands/pip_compile.rs b/crates/puffin-cli/src/commands/pip_compile.rs index 934ded17910ed..b943db7060262 100644 --- a/crates/puffin-cli/src/commands/pip_compile.rs +++ b/crates/puffin-cli/src/commands/pip_compile.rs @@ -125,6 +125,7 @@ pub(crate) async fn pip_compile( || Cow::Borrowed(venv.interpreter_info().markers()), |python_version| Cow::Owned(python_version.markers(venv.interpreter_info().markers())), ); + // Inject the fake python version if necessary let interpreter_info = venv .interpreter_info() .clone() diff --git a/crates/puffin-cli/tests/pip_compile.rs b/crates/puffin-cli/tests/pip_compile.rs index 27a30452e137b..e75427f9374fc 100644 --- a/crates/puffin-cli/tests/pip_compile.rs +++ b/crates/puffin-cli/tests/pip_compile.rs @@ -669,7 +669,7 @@ fn compile_numpy_py37() -> Result<()> { ----- stdout ----- # This file was autogenerated by Puffin v0.0.1 via the following command: # [BIN_PATH] pip-compile requirements.in --python-version py37 --cache-dir [CACHE_DIR] - numpy==1.26.1 + numpy==1.26.2 ----- stderr ----- Resolved 1 package in [TIME] diff --git a/crates/puffin-distribution/src/traits.rs b/crates/puffin-distribution/src/traits.rs index 858f228b15060..a40bdda6a3ee1 100644 --- a/crates/puffin-distribution/src/traits.rs +++ b/crates/puffin-distribution/src/traits.rs @@ -12,8 +12,8 @@ pub trait Metadata { /// Return the normalized [`PackageName`] of the distribution. fn name(&self) -> &PackageName; - /// Return a [`Version`], for registry-based distributions, or a [`Url`], for URL-based - /// distributions. + /// Return a [`pep440_rs::Version`], for registry-based distributions, or a [`url::Url`], + /// for URL-based distributions. fn version_or_url(&self) -> VersionOrUrl; /// Returns a unique identifier for the package. diff --git a/crates/puffin-resolver/src/finder.rs b/crates/puffin-resolver/src/finder.rs index 45b9caa28c489..ab399b51ef4ed 100644 --- a/crates/puffin-resolver/src/finder.rs +++ b/crates/puffin-resolver/src/finder.rs @@ -10,7 +10,6 @@ use futures::{StreamExt, TryFutureExt}; use fxhash::FxHashMap; use distribution_filename::{SourceDistFilename, WheelFilename}; -use pep440_rs::VersionSpecifiers; use pep508_rs::{Requirement, VersionOrUrl}; use platform_tags::Tags; use puffin_client::RegistryClient; @@ -148,13 +147,12 @@ impl<'a> DistFinder<'a> { SourceDistFilename::parse(file.filename.as_str(), &requirement.name) { // Only add source dists compatible with the python version - // https://github.com/astral-sh/puffin/issues/388 + // TODO(konstin): https://github.com/astral-sh/puffin/issues/406 if file .requires_python - .clone() + .as_ref() .map_or(true, |requires_python| { - VersionSpecifiers::from(requires_python) - .contains(self.interpreter_info.version()) + requires_python.contains(self.interpreter_info.version()) }) { if requirement.is_satisfied_by(&sdist.version) { diff --git a/crates/puffin-resolver/src/resolver.rs b/crates/puffin-resolver/src/resolver.rs index 0d922e341652a..20543c5772118 100644 --- a/crates/puffin-resolver/src/resolver.rs +++ b/crates/puffin-resolver/src/resolver.rs @@ -18,7 +18,6 @@ use url::Url; use waitmap::WaitMap; use distribution_filename::{SourceDistFilename, WheelFilename}; -use pep440_rs::VersionSpecifiers; use pep508_rs::{MarkerEnvironment, Requirement}; use platform_tags::Tags; use puffin_cache::CanonicalUrl; @@ -556,12 +555,12 @@ impl<'a, Context: BuildContext + Sync> Resolver<'a, Context> { SourceDistFilename::parse(file.filename.as_str(), &package_name) { // Only add source dists compatible with the python version - // https://github.com/astral-sh/puffin/issues/388 + // TODO(konstin): https://github.com/astral-sh/puffin/issues/406 if file .requires_python - .clone() + .as_ref() .map_or(true, |requires_python| { - VersionSpecifiers::from(requires_python) + requires_python .contains(self.build_context.interpreter_info().version()) }) { diff --git a/crates/pypi-types/src/lenient_requirement.rs b/crates/pypi-types/src/lenient_requirement.rs index d3a5a9fe0bf5e..fc597682e0f36 100644 --- a/crates/pypi-types/src/lenient_requirement.rs +++ b/crates/pypi-types/src/lenient_requirement.rs @@ -4,6 +4,8 @@ use std::str::FromStr; use tracing::warn; /// Like [`VersionSpecifiers`], but attempts to correct some common errors in user-provided requirements. +/// +/// We turn `>=3.x.*` into `>=3.x` #[derive(Debug, Clone, Serialize, Eq, PartialEq)] pub struct LenientVersionSpecifiers(VersionSpecifiers); @@ -57,6 +59,6 @@ impl<'de> Deserialize<'de> for LenientVersionSpecifiers { D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; - FromStr::from_str(&s).map_err(de::Error::custom) + Self::from_str(&s).map_err(de::Error::custom) } } diff --git a/crates/pypi-types/src/lib.rs b/crates/pypi-types/src/lib.rs index 926e38de342f0..6fccd14755eca 100644 --- a/crates/pypi-types/src/lib.rs +++ b/crates/pypi-types/src/lib.rs @@ -1,4 +1,5 @@ pub use direct_url::{ArchiveInfo, DirectUrl, VcsInfo, VcsKind}; +pub use lenient_requirement::LenientVersionSpecifiers; pub use metadata::{Error, Metadata21}; pub use simple_json::{File, SimpleJson}; diff --git a/crates/pypi-types/src/simple_json.rs b/crates/pypi-types/src/simple_json.rs index 4b02bdb97c704..56138b8f3b0d8 100644 --- a/crates/pypi-types/src/simple_json.rs +++ b/crates/pypi-types/src/simple_json.rs @@ -1,4 +1,6 @@ -use serde::{Deserialize, Serialize}; +use pep440_rs::VersionSpecifiers; +use serde::{de, Deserialize, Deserializer, Serialize}; +use std::str::FromStr; use crate::lenient_requirement::LenientVersionSpecifiers; @@ -17,13 +19,30 @@ pub struct File { pub data_dist_info_metadata: Metadata, pub filename: String, pub hashes: Hashes, - pub requires_python: Option, + /// Note: Deserialized with [`LenientVersionSpecifiers`] since there are a number of invalid + /// versions on pypi + #[serde(deserialize_with = "deserialize_version_specifiers_lenient")] + pub requires_python: Option, pub size: usize, pub upload_time: String, pub url: String, pub yanked: Yanked, } +fn deserialize_version_specifiers_lenient<'de, D>( + deserializer: D, +) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let maybe_string: Option = Option::deserialize(deserializer)?; + let Some(string) = maybe_string else { + return Ok(None); + }; + let lenient = LenientVersionSpecifiers::from_str(&string).map_err(de::Error::custom)?; + Ok(Some(lenient.into())) +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum Metadata {