mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-28 04:54:47 +00:00
Ignore invalid extra named .none
(#1428)
## Summary Some packages erroneously include an extra named `.none`. It turns out that certain versions of `flit` included this by accident: https://github.com/pypa/flit/issues/228/. This PR adds leniency for that specific extra name. Closes https://github.com/astral-sh/uv/issues/1363. Closes https://github.com/astral-sh/uv/issues/1399.
This commit is contained in:
parent
0bfce353fb
commit
958e88ddbf
4 changed files with 68 additions and 2 deletions
|
@ -7,6 +7,7 @@ use tracing::warn;
|
||||||
|
|
||||||
use pep440_rs::{VersionSpecifiers, VersionSpecifiersParseError};
|
use pep440_rs::{VersionSpecifiers, VersionSpecifiersParseError};
|
||||||
use pep508_rs::{Pep508Error, Requirement};
|
use pep508_rs::{Pep508Error, Requirement};
|
||||||
|
use uv_normalize::{ExtraName, InvalidNameError};
|
||||||
|
|
||||||
/// Ex) `>=7.2.0<8.0.0`
|
/// Ex) `>=7.2.0<8.0.0`
|
||||||
static MISSING_COMMA: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\d)([<>=~^!])").unwrap());
|
static MISSING_COMMA: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\d)([<>=~^!])").unwrap());
|
||||||
|
@ -122,6 +123,36 @@ impl<'de> Deserialize<'de> for LenientVersionSpecifiers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LenientExtraName(ExtraName);
|
||||||
|
|
||||||
|
impl LenientExtraName {
|
||||||
|
/// Parse an [`ExtraName`] from a string, but return `None` if the name is `.none`.
|
||||||
|
///
|
||||||
|
/// Some versions of `flit` erroneously included `.none` as an extra name, which is not
|
||||||
|
/// allowed by PEP 508.
|
||||||
|
///
|
||||||
|
/// See: <https://github.com/pypa/flit/issues/228/>
|
||||||
|
pub fn try_parse(name: String) -> Option<Result<Self, InvalidNameError>> {
|
||||||
|
match ExtraName::new(name) {
|
||||||
|
Ok(name) => Some(Ok(Self(name))),
|
||||||
|
Err(err) => {
|
||||||
|
if err.as_str() == ".none" {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Err(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LenientExtraName> for ExtraName {
|
||||||
|
fn from(name: LenientExtraName) -> Self {
|
||||||
|
name.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
|
@ -12,7 +12,7 @@ use pep508_rs::{Pep508Error, Requirement};
|
||||||
use uv_normalize::{ExtraName, InvalidNameError, PackageName};
|
use uv_normalize::{ExtraName, InvalidNameError, PackageName};
|
||||||
|
|
||||||
use crate::lenient_requirement::LenientRequirement;
|
use crate::lenient_requirement::LenientRequirement;
|
||||||
use crate::LenientVersionSpecifiers;
|
use crate::{LenientExtraName, LenientVersionSpecifiers};
|
||||||
|
|
||||||
/// Python Package Metadata 2.1 as specified in
|
/// Python Package Metadata 2.1 as specified in
|
||||||
/// <https://packaging.python.org/specifications/core-metadata/>.
|
/// <https://packaging.python.org/specifications/core-metadata/>.
|
||||||
|
@ -119,7 +119,11 @@ impl Metadata21 {
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let provides_extras = get_all_values("Provides-Extra")
|
let provides_extras = get_all_values("Provides-Extra")
|
||||||
.map(ExtraName::new)
|
.filter_map(LenientExtraName::try_parse)
|
||||||
|
.map(|result| match result {
|
||||||
|
Ok(extra_name) => Ok(ExtraName::from(extra_name)),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
Ok(Metadata21 {
|
Ok(Metadata21 {
|
||||||
|
|
|
@ -93,6 +93,13 @@ fn is_normalized(name: impl AsRef<str>) -> Result<bool, InvalidNameError> {
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct InvalidNameError(String);
|
pub struct InvalidNameError(String);
|
||||||
|
|
||||||
|
impl InvalidNameError {
|
||||||
|
/// Returns the invalid name.
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for InvalidNameError {
|
impl Display for InvalidNameError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
|
|
|
@ -3352,3 +3352,27 @@ fn compile_relative_subfile() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a package with an invalid extra named `.none`.
|
||||||
|
#[test]
|
||||||
|
fn compile_none_extra() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
let requirements_in = context.temp_dir.child("requirements.in");
|
||||||
|
requirements_in.write_str("entrypoints==0.3")?;
|
||||||
|
|
||||||
|
uv_snapshot!(context
|
||||||
|
.compile()
|
||||||
|
.arg("requirements.in"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by uv v[VERSION] via the following command:
|
||||||
|
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z requirements.in
|
||||||
|
entrypoints==0.3
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue