diff --git a/crates/pypi-types/src/lenient_requirement.rs b/crates/pypi-types/src/lenient_requirement.rs index fc597682e..867cccb9d 100644 --- a/crates/pypi-types/src/lenient_requirement.rs +++ b/crates/pypi-types/src/lenient_requirement.rs @@ -31,6 +31,20 @@ impl FromStr for LenientVersionSpecifiers { ">=3.2.*" => Some(">=3.2"), ">=3.1.*" => Some(">=3.1"), ">=3.0.*" => Some(">=3.0"), + ">=3.12," => Some(">=3.12"), + ">=3.11," => Some(">=3.11"), + ">=3.10," => Some(">=3.10"), + ">=3.9," => Some(">=3.9"), + ">=3.8," => Some(">=3.8"), + ">=3.7," => Some(">=3.7"), + ">=3.6," => Some(">=3.6"), + ">=3.5," => Some(">=3.5"), + ">=3.4," => Some(">=3.4"), + ">=3.3," => Some(">=3.3"), + ">=3.2," => Some(">=3.2"), + ">=3.1," => Some(">=3.1"), + ">=3.0," => Some(">=3.0"), + ">=2.7,!=3.0*,!=3.1*,!=3.2*" => Some(">=2.7,!=3.0.*,!=3.1.*,!=3.2.*"), _ => None, }; if let Some(patched) = patched { diff --git a/crates/pypi-types/src/metadata.rs b/crates/pypi-types/src/metadata.rs index 85ed37c04..72b406a2e 100644 --- a/crates/pypi-types/src/metadata.rs +++ b/crates/pypi-types/src/metadata.rs @@ -219,6 +219,8 @@ static NOT_EQUAL_TILDE: Lazy = Lazy::new(|| Regex::new(r"!=~((?:\d\.)*\d) static GREATER_THAN_STAR: Lazy = Lazy::new(|| Regex::new(r">=(\d+\.\d+)\.\*").unwrap()); /// Ex) `!=3.0*` static MISSING_DOT: Lazy = Lazy::new(|| Regex::new(r"(\d\.\d)+\*").unwrap()); +/// Ex) `>=3.6,` +static TRAILING_COMMA: Lazy = Lazy::new(|| Regex::new(r",\)").unwrap()); /// Like [`Requirement`], but attempts to correct some common errors in user-provided requirements. #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] @@ -253,7 +255,7 @@ impl FromStr for LenientRequirement { } } - // Given `torch (>=1.9.*)`, rewrite to `torch (>=1.9)` + // Given `torch (>=1.9.*)`, rewrite to `torch (>=1.9)`. let patched = GREATER_THAN_STAR.replace_all(s, r">=${1}"); if patched != s { if let Ok(requirement) = Requirement::from_str(&patched) { @@ -264,7 +266,7 @@ impl FromStr for LenientRequirement { } } - // Given `pyzmq (!=3.0*)`, rewrite to `pyzmq (!=3.0.*)` + // Given `pyzmq (!=3.0*)`, rewrite to `pyzmq (!=3.0.*)`. let patched = MISSING_DOT.replace_all(s, r"${1}.*"); if patched != s { if let Ok(requirement) = Requirement::from_str(&patched) { @@ -275,6 +277,17 @@ impl FromStr for LenientRequirement { } } + // Given `pyzmq (>=3.6,)`, rewrite to `pyzmq (>=3.6)` + let patched = TRAILING_COMMA.replace_all(s, r")"); + if patched != s { + if let Ok(requirement) = Requirement::from_str(&patched) { + warn!( + "Removing trailing comma from invalid requirement (before: `{s}`; after: `{patched}`)", + ); + return Ok(Self(requirement)); + } + } + Err(err) } } @@ -339,4 +352,13 @@ mod tests { Requirement::from_str("pyzmq (>=2.7,!=3.0.*,!=3.1.*,!=3.2.*)").unwrap(); assert_eq!(actual, expected); } + + #[test] + fn trailing_comma() { + let actual: Requirement = LenientRequirement::from_str("pyzmq (>=3.6,)") + .unwrap() + .into(); + let expected: Requirement = Requirement::from_str("pyzmq (>=3.6)").unwrap(); + assert_eq!(actual, expected); + } }